View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
4    *
5    *  Licensed to the Apache Software Foundation (ASF) under one
6    *  or more contributor license agreements.  See the NOTICE file
7    *  distributed with this work for additional information
8    *  regarding copyright ownership.  The ASF licenses this file
9    *  to you under the Apache License, Version 2.0 (the
10   *  "License"); you may not use this file except in compliance
11   *  with the License.  You may obtain a copy of the License at
12   *
13   *    http://www.apache.org/licenses/LICENSE-2.0
14   *
15   *  Unless required by applicable law or agreed to in writing,
16   *  software distributed under the License is distributed on an
17   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   *  KIND, either express or implied.  See the License for the
19   *  specific language governing permissions and limitations
20   *  under the License.
21   *
22   *  @version $Id: BluetoothStackWIDCOMM.java 2559 2008-12-11 08:50:08Z skarzhevskyy $
23   */
24  package com.intel.bluetooth;
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.ByteArrayOutputStream;
28  import java.io.IOException;
29  import java.util.Enumeration;
30  import java.util.Hashtable;
31  import java.util.Vector;
32  
33  import javax.bluetooth.BluetoothStateException;
34  import javax.bluetooth.DataElement;
35  import javax.bluetooth.DeviceClass;
36  import javax.bluetooth.DiscoveryAgent;
37  import javax.bluetooth.DiscoveryListener;
38  import javax.bluetooth.RemoteDevice;
39  import javax.bluetooth.ServiceRecord;
40  import javax.bluetooth.ServiceRegistrationException;
41  import javax.bluetooth.UUID;
42  
43  class BluetoothStackWIDCOMM implements BluetoothStack {
44  
45      private static BluetoothStackWIDCOMM singleInstance = null;
46  
47      private boolean initialized = false;
48  
49      private Vector deviceDiscoveryListeners = new Vector/* <DiscoveryListener> */();
50  
51      private Hashtable deviceDiscoveryListenerFoundDevices = new Hashtable();
52  
53      private Hashtable deviceDiscoveryListenerReportedDevices = new Hashtable();
54  
55      // TODO what is the real number for Attributes retrievable ?
56      private final static int ATTR_RETRIEVABLE_MAX = 256;
57  
58      private final static int RECEIVE_MTU_MAX = 1024;
59  
60      // from WIDCOMM BtIfDefinitions.h
61      final static short NULL_DESC_TYPE = 0;
62  
63      final static short UINT_DESC_TYPE = 1;
64  
65      final static short TWO_COMP_INT_DESC_TYPE = 2;
66  
67      final static short UUID_DESC_TYPE = 3;
68  
69      final static short TEXT_STR_DESC_TYPE = 4;
70  
71      final static short BOOLEAN_DESC_TYPE = 5;
72  
73      final static short DATA_ELE_SEQ_DESC_TYPE = 6;
74  
75      final static short DATA_ELE_ALT_DESC_TYPE = 7;
76  
77      final static short URL_DESC_TYPE = 8;
78  
79      BluetoothStackWIDCOMM() {
80      }
81  
82      public String getStackID() {
83          return BlueCoveImpl.STACK_WIDCOMM;
84      }
85  
86      public String toString() {
87          return getStackID();
88      }
89  
90      /*
91       * (non-Javadoc)
92       * 
93       * @see com.intel.bluetooth.BluetoothStack#getFeatureSet()
94       */
95      public int getFeatureSet() {
96          return FEATURE_SERVICE_ATTRIBUTES | FEATURE_L2CAP;
97      }
98  
99      // ---------------------- Library initialization
100 
101     /*
102      * (non-Javadoc)
103      * 
104      * @see com.intel.bluetooth.BluetoothStack#isNativeCodeLoaded()
105      */
106     public native boolean isNativeCodeLoaded();
107 
108     /*
109      * (non-Javadoc)
110      * 
111      * @see com.intel.bluetooth.BluetoothStack#requireNativeLibraries()
112      */
113     public LibraryInformation[] requireNativeLibraries() {
114         return LibraryInformation.library(BlueCoveImpl.NATIVE_LIB_WIDCOMM);
115     }
116 
117     public native int getLibraryVersion();
118 
119     public native int detectBluetoothStack();
120 
121     public native void enableNativeDebug(Class nativeDebugCallback, boolean on);
122 
123     public void initialize() throws BluetoothStateException {
124         if (singleInstance != null) {
125             throw new BluetoothStateException("Only one instance of " + getStackID() + " stack supported");
126         }
127         if (!initializeImpl()) {
128             throw new RuntimeException("WIDCOMM BluetoothStack not found");
129         }
130         initialized = true;
131         singleInstance = this;
132     }
133 
134     public native boolean initializeImpl();
135 
136     private native void uninitialize();
137 
138     public void destroy() {
139         if (singleInstance != this) {
140             throw new RuntimeException("Destroy invalid instance");
141         }
142         if (initialized) {
143             uninitialize();
144             initialized = false;
145             DebugLog.debug("WIDCOMM destroyed");
146         }
147         singleInstance = null;
148     }
149 
150     protected void finalize() {
151         destroy();
152     }
153 
154     public native String getLocalDeviceBluetoothAddress() throws BluetoothStateException;
155 
156     public native String getLocalDeviceName();
157 
158     private native int getDeviceClassImpl();
159 
160     /**
161      * There are no functions to set WIDCOMM stack
162      */
163     public DeviceClass getLocalDeviceClass() {
164         return new DeviceClass(getDeviceClassImpl());
165     }
166 
167     /*
168      * (non-Javadoc)
169      * 
170      * @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int)
171      */
172     public void setLocalDeviceServiceClasses(int classOfDevice) {
173         throw new NotSupportedRuntimeException(getStackID());
174     }
175 
176     /**
177      * There are no functions to set WIDCOMM stack discoverable status.
178      * 
179      * @return <code>true</code> if the request succeeded, otherwise
180      *         <code>false</code> if the request failed because the BCC denied
181      *         the request; <code>false</code> if the Bluetooth system does not
182      *         support the access mode specified in <code>mode</code>
183      */
184     public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException {
185         int curentMode = getLocalDeviceDiscoverable();
186         if (curentMode == mode) {
187             return true;
188         } else {
189             return false;
190         }
191     }
192 
193     private native boolean isStackServerUp();
194 
195     private synchronized void verifyDeviceReady() throws BluetoothStateException {
196         if (!isLocalDevicePowerOn()) {
197             throw new BluetoothStateException("Bluetooth Device is not ready");
198         }
199     }
200 
201     public native boolean isLocalDeviceDiscoverable();
202 
203     public int getLocalDeviceDiscoverable() {
204         if (isStackServerUp() && isLocalDeviceDiscoverable()) {
205             return DiscoveryAgent.GIAC;
206         } else {
207             return DiscoveryAgent.NOT_DISCOVERABLE;
208         }
209     }
210 
211     public native boolean isLocalDevicePowerOn();
212 
213     private native String getBTWVersionInfo();
214 
215     private native int getDeviceVersion();
216 
217     private native int getDeviceManufacturer();
218 
219     public String getLocalDeviceProperty(String property) {
220         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_DEVICES_MAX.equals(property)) {
221             return "7";
222         }
223         if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_TRANS_MAX.equals(property)) {
224             return "1";
225         }
226         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY_SCAN.equals(property)) {
227             return BlueCoveImpl.TRUE;
228         }
229         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE_SCAN.equals(property)) {
230             return BlueCoveImpl.TRUE;
231         }
232         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY.equals(property)) {
233             return BlueCoveImpl.TRUE;
234         }
235         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE.equals(property)) {
236             return BlueCoveImpl.TRUE;
237         }
238 
239         if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_ATTR_RETRIEVABLE_MAX.equals(property)) {
240             return String.valueOf(ATTR_RETRIEVABLE_MAX);
241         }
242         if (BluetoothConsts.PROPERTY_BLUETOOTH_MASTER_SWITCH.equals(property)) {
243             return BlueCoveImpl.FALSE;
244         }
245         if (BluetoothConsts.PROPERTY_BLUETOOTH_L2CAP_RECEIVEMTU_MAX.equals(property)) {
246             return String.valueOf(RECEIVE_MTU_MAX);
247         }
248 
249         if ("bluecove.radio.version".equals(property)) {
250             return String.valueOf(getDeviceVersion());
251         }
252         if ("bluecove.radio.manufacturer".equals(property)) {
253             return String.valueOf(getDeviceManufacturer());
254         }
255         if ("bluecove.stack.version".equals(property)) {
256             return getBTWVersionInfo();
257         }
258         // Some Hack and testing functions, not documented
259         if (property.startsWith("bluecove.nativeFunction:")) {
260             String functionDescr = property.substring(property.indexOf(':') + 1, property.length());
261             int paramIdx = functionDescr.indexOf(':');
262             if (paramIdx == -1) {
263                 throw new RuntimeException("Invalid native function " + functionDescr + "; arguments expected");
264             }
265             String function = functionDescr.substring(0, paramIdx);
266             long address = RemoteDeviceHelper.getAddress(functionDescr.substring(function.length() + 1, functionDescr.length()));
267             if ("getRemoteDeviceVersionInfo".equals(function)) {
268                 return getRemoteDeviceVersionInfo(address);
269             } else if ("cancelSniffMode".equals(function)) {
270                 return String.valueOf(cancelSniffMode(address));
271             } else if ("setSniffMode".equals(function)) {
272                 return String.valueOf(setSniffMode(address));
273             } else if ("getRemoteDeviceRSSI".equals(function)) {
274                 return String.valueOf(getRemoteDeviceRSSI(address));
275             } else if ("getRemoteDeviceLinkMode".equals(function)) {
276                 if (isRemoteDeviceConnected(address)) {
277                     return getRemoteDeviceLinkMode(address);
278                 } else {
279                     return "disconnected";
280                 }
281             }
282         }
283         return null;
284     }
285 
286     /*
287      * (non-Javadoc)
288      * 
289      * @see
290      * com.intel.bluetooth.BluetoothStack#isCurrentThreadInterruptedCallback()
291      */
292     public boolean isCurrentThreadInterruptedCallback() {
293         return UtilsJavaSE.isCurrentThreadInterrupted();
294     }
295 
296     public RemoteDevice[] retrieveDevices(int option) {
297         return null;
298     }
299 
300     public Boolean isRemoteDeviceTrusted(long address) {
301         return null;
302     }
303 
304     public Boolean isRemoteDeviceAuthenticated(long address) {
305         return null;
306     }
307 
308     // ---------------------- Remote Device authentication
309 
310     public boolean authenticateRemoteDevice(long address) throws IOException {
311         return false;
312     }
313 
314     private native boolean authenticateRemoteDeviceImpl(long address, String passkey) throws IOException;
315 
316     /*
317      * (non-Javadoc)
318      * 
319      * @see com.intel.bluetooth.BluetoothStack#authenticateRemoteDevice(long,
320      * java.lang.String)
321      */
322     public boolean authenticateRemoteDevice(long address, String passkey) throws IOException {
323         return authenticateRemoteDeviceImpl(address, passkey);
324     }
325 
326     private native void removeAuthenticationWithRemoteDeviceImpl(long address) throws IOException;
327 
328     /*
329      * (non-Javadoc)
330      * 
331      * @see
332      * com.intel.bluetooth.BluetoothStack#removeAuthenticationWithRemoteDevice
333      * (long)
334      */
335     public void removeAuthenticationWithRemoteDevice(long address) throws IOException {
336         removeAuthenticationWithRemoteDeviceImpl(address);
337     }
338 
339     // --- Some testing functions accessible by LocalDevice.getProperty
340 
341     private native boolean isRemoteDeviceConnected(long address);
342 
343     private native String getRemoteDeviceLinkMode(long address);
344 
345     private native String getRemoteDeviceVersionInfo(long address);
346 
347     private native boolean setSniffMode(long address);
348 
349     private native boolean cancelSniffMode(long address);
350 
351     private native int getRemoteDeviceRSSI(long address);
352 
353     // --- Device Inquiry
354 
355     private native int runDeviceInquiryImpl(DeviceInquiryRunnable inquiryRunnable, DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener)
356             throws BluetoothStateException;
357 
358     public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException {
359         deviceDiscoveryListeners.addElement(listener);
360         if (BlueCoveImpl.getConfigProperty(BlueCoveConfigProperties.PROPERTY_INQUIRY_REPORT_ASAP, false)) {
361             deviceDiscoveryListenerFoundDevices.put(listener, new Hashtable());
362         }
363         deviceDiscoveryListenerReportedDevices.put(listener, new Vector());
364         DeviceInquiryRunnable inquiryRunnable = new DeviceInquiryRunnable() {
365             public int runDeviceInquiry(DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener) throws BluetoothStateException {
366                 try {
367                     int discType = runDeviceInquiryImpl(this, startedNotify, accessCode, listener);
368                     if (discType == DiscoveryListener.INQUIRY_COMPLETED) {
369                         // Report found devices if any not reported
370                         Hashtable previouslyFound = (Hashtable) deviceDiscoveryListenerFoundDevices.get(listener);
371                         if (previouslyFound != null) {
372                             Vector reported = (Vector) deviceDiscoveryListenerReportedDevices.get(listener);
373                             for (Enumeration en = previouslyFound.keys(); en.hasMoreElements();) {
374                                 RemoteDevice remoteDevice = (RemoteDevice) en.nextElement();
375                                 if (reported.contains(remoteDevice)) {
376                                     continue;
377                                 }
378                                 reported.addElement(remoteDevice);
379                                 Integer deviceClassInt = (Integer) previouslyFound.get(remoteDevice);
380                                 DeviceClass deviceClass = new DeviceClass(deviceClassInt.intValue());
381                                 listener.deviceDiscovered(remoteDevice, deviceClass);
382                                 // If cancelInquiry has been called
383                                 if (!deviceDiscoveryListeners.contains(listener)) {
384                                     return DiscoveryListener.INQUIRY_TERMINATED;
385                                 }
386                             }
387                         }
388                     }
389                     return discType;
390                 } finally {
391                     deviceDiscoveryListeners.removeElement(listener);
392                     deviceDiscoveryListenerFoundDevices.remove(listener);
393                     deviceDiscoveryListenerReportedDevices.remove(listener);
394                 }
395             }
396 
397             /*
398              * This function may trigger multiple times per inquiry – even
399              * multiple times per device – once for the address alone, and once
400              * for the address and the user-friendly name.
401              */
402             public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass, String deviceName, boolean paired) {
403                 DebugLog.debug("deviceDiscoveredCallback deviceName", deviceName);
404                 if (!deviceDiscoveryListeners.contains(listener)) {
405                     return;
406                 }
407                 // Update name if name retrieved
408                 RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(BluetoothStackWIDCOMM.this, deviceAddr, deviceName, paired);
409                 Vector reported = (Vector) deviceDiscoveryListenerReportedDevices.get(listener);
410                 if (reported == null || (reported.contains(remoteDevice))) {
411                     return;
412                 }
413                 // See -Dbluecove.inquiry.report_asap=false
414                 Hashtable previouslyFound = (Hashtable) deviceDiscoveryListenerFoundDevices.get(listener);
415                 if (previouslyFound != null) {
416                     Integer deviceClassInt = (Integer) previouslyFound.get(remoteDevice);
417                     if (deviceClassInt == null) {
418                         previouslyFound.put(remoteDevice, new Integer(deviceClass));
419                     } else if (deviceClass != 0) {
420                         previouslyFound.put(remoteDevice, new Integer(deviceClass));
421                     }
422                 } else {
423                     DeviceClass cod = new DeviceClass(deviceClass);
424                     reported.addElement(remoteDevice);
425                     DebugLog.debug("deviceDiscoveredCallback address", remoteDevice.getBluetoothAddress());
426                     DebugLog.debug("deviceDiscoveredCallback deviceClass", cod);
427                     listener.deviceDiscovered(remoteDevice, cod);
428                 }
429             }
430         };
431         return DeviceInquiryThread.startInquiry(this, inquiryRunnable, accessCode, listener);
432     }
433 
434     private native boolean deviceInquiryCancelImpl();
435 
436     public boolean cancelInquiry(DiscoveryListener listener) {
437         // no further deviceDiscovered() events will occur for this inquiry
438         if (!deviceDiscoveryListeners.removeElement(listener)) {
439             return false;
440         }
441         return deviceInquiryCancelImpl();
442     }
443 
444     native String getRemoteDeviceFriendlyName(long address, int majorDeviceClass, int minorDeviceClass) throws IOException;
445 
446     /**
447      * get device name while discovery running. Device may not report its name
448      * first time while discovering.
449      * 
450      * @param address
451      * @return name
452      */
453     native String peekRemoteDeviceFriendlyName(long address);
454 
455     public String getRemoteDeviceFriendlyName(long address) throws IOException {
456         if (deviceDiscoveryListeners.size() != 0) {
457             // discovery running
458             return peekRemoteDeviceFriendlyName(address);
459         } else {
460             // Another way to get name is to run deviceInquiry
461             DiscoveryListener listener = new DiscoveryListenerAdapter();
462             if (startInquiry(DiscoveryAgent.GIAC, listener)) {
463                 String name = peekRemoteDeviceFriendlyName(address);
464                 cancelInquiry(listener);
465                 return name;
466             }
467         }
468         return null;
469     }
470 
471     // --- Service search
472 
473     private native long[] runSearchServicesImpl(SearchServicesThread startedNotify, byte[] uuidValue, long address) throws BluetoothStateException,
474             SearchServicesTerminatedException;
475 
476     public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException {
477 
478         SearchServicesRunnable searchRunnable = new SearchServicesRunnable() {
479 
480             public int runSearchServices(SearchServicesThread sst, int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
481                     throws BluetoothStateException {
482                 // Retrieve all Records, Filter here in Java
483                 synchronized (BluetoothStackWIDCOMM.class) {
484                     byte[] uuidValue = Utils.UUIDToByteArray(BluetoothConsts.L2CAP_PROTOCOL_UUID);
485                     for (int u = 0; u < uuidSet.length; u++) {
486                         if (uuidSet[u].equals(BluetoothConsts.L2CAP_PROTOCOL_UUID)) {
487                             continue;
488                         } else if (uuidSet[u].equals(BluetoothConsts.RFCOMM_PROTOCOL_UUID)) {
489                             uuidValue = Utils.UUIDToByteArray(uuidSet[u]);
490                             continue;
491                         } else {
492                             // Look for the most specific UUID
493                             uuidValue = Utils.UUIDToByteArray(uuidSet[u]);
494                             break;
495                         }
496                     }
497                     long[] handles;
498                     try {
499                         handles = runSearchServicesImpl(sst, uuidValue, RemoteDeviceHelper.getAddress(device));
500                     } catch (SearchServicesTerminatedException e) {
501                         DebugLog.debug("SERVICE_SEARCH_TERMINATED");
502                         return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
503                     }
504                     if (handles == null) {
505                         DebugLog.debug("SERVICE_SEARCH_ERROR");
506                         return DiscoveryListener.SERVICE_SEARCH_ERROR;
507                     } else if (handles.length > 0) {
508                         Vector records = new Vector();
509                         int[] uuidFilerAttrIDs = new int[] { BluetoothConsts.ServiceClassIDList, BluetoothConsts.ProtocolDescriptorList };
510                         int[] requiredAttrIDs = new int[] { BluetoothConsts.ServiceRecordHandle, BluetoothConsts.ServiceRecordState, BluetoothConsts.ServiceID };
511                         nextRecord: for (int i = 0; i < handles.length; i++) {
512                             ServiceRecordImpl sr = new ServiceRecordImpl(BluetoothStackWIDCOMM.this, device, handles[i]);
513                             try {
514                                 sr.populateRecord(uuidFilerAttrIDs);
515                                 // Apply JSR-82 filter, all UUID should be present
516                                 for (int u = 0; u < uuidSet.length; u++) {
517                                     if (!((sr.hasServiceClassUUID(uuidSet[u]) || sr.hasProtocolClassUUID(uuidSet[u])))) {
518                                         if (BluetoothStackWIDCOMMSDPInputStream.debug) {
519                                             DebugLog.debug("filtered ServiceRecord (" + i + ")", sr);
520                                         }
521                                         continue nextRecord;
522                                     }
523                                 }
524                                 if (BluetoothStackWIDCOMMSDPInputStream.debug) {
525                                     DebugLog.debug("accepted ServiceRecord (" + i + ")", sr);
526                                 }
527                                 if (!isServiceRecordDiscoverable(RemoteDeviceHelper.getAddress(device), sr.getHandle())) {
528                                     continue;
529                                 }
530 
531                                 records.addElement(sr);
532                                 sr.populateRecord(requiredAttrIDs);
533                                 if (attrSet != null) {
534                                     sr.populateRecord(attrSet);
535                                 }
536                                 DebugLog.debug("ServiceRecord (" + i + ") sr.handle", handles[i]);
537                                 DebugLog.debug("ServiceRecord (" + i + ")", sr);
538                             } catch (Exception e) {
539                                 DebugLog.debug("populateRecord error", e);
540                             }
541                             if (sst.isTerminated()) {
542                                 DebugLog.debug("SERVICE_SEARCH_TERMINATED");
543                                 return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
544                             }
545                         }
546                         if (records.size() != 0) {
547                             DebugLog.debug("SERVICE_SEARCH_COMPLETED");
548                             ServiceRecord[] fileteredRecords = (ServiceRecord[]) Utils.vector2toArray(records, new ServiceRecord[records.size()]);
549                             listener.servicesDiscovered(sst.getTransID(), fileteredRecords);
550                             return DiscoveryListener.SERVICE_SEARCH_COMPLETED;
551                         }
552                     }
553                     DebugLog.debug("SERVICE_SEARCH_NO_RECORDS");
554                     return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS;
555                 }
556             }
557         };
558         return SearchServicesThread.startSearchServices(this, searchRunnable, attrSet, uuidSet, device, listener);
559     }
560 
561     /**
562      * Only one concurrent ServiceSearch supported
563      */
564     private native void cancelServiceSearchImpl();
565 
566     public boolean cancelServiceSearch(int transID) {
567         SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID);
568         if (sst != null) {
569             synchronized (this) {
570                 if (!sst.isTerminated()) {
571                     sst.setTerminated();
572                     cancelServiceSearchImpl();
573                     return true;
574                 }
575             }
576         }
577         return false;
578     }
579 
580     private native byte[] getServiceAttribute(int attrID, long handle) throws IOException;
581 
582     private native boolean isServiceRecordDiscoverable(long address, long handle) throws IOException;
583 
584     public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs) throws IOException {
585         if (attrIDs.length > ATTR_RETRIEVABLE_MAX) {
586             throw new IllegalArgumentException();
587         }
588         boolean anyRetrived = false;
589         for (int i = 0; i < attrIDs.length; i++) {
590             int id = attrIDs[i];
591             try {
592                 byte[] sdpStruct = getServiceAttribute(id, serviceRecord.getHandle());
593                 if (sdpStruct != null) {
594                     if (BluetoothStackWIDCOMMSDPInputStream.debug) {
595                         DebugLog.debug("decode attribute " + id + " Ox" + Integer.toHexString(id));
596                     }
597                     DataElement element = (new BluetoothStackWIDCOMMSDPInputStream(new ByteArrayInputStream(sdpStruct))).readElement();
598 
599                     // Do special case conversion for only one element in the
600                     // list.
601                     if (id == BluetoothConsts.ProtocolDescriptorList) {
602                         Enumeration protocolsSeqEnum = (Enumeration) element.getValue();
603                         if (protocolsSeqEnum.hasMoreElements()) {
604                             DataElement protocolElement = (DataElement) protocolsSeqEnum.nextElement();
605                             if (protocolElement.getDataType() != DataElement.DATSEQ) {
606                                 DataElement newMainSeq = new DataElement(DataElement.DATSEQ);
607                                 newMainSeq.addElement(element);
608                                 element = newMainSeq;
609                             }
610                         }
611                     }
612 
613                     serviceRecord.populateAttributeValue(id, element);
614                     anyRetrived = true;
615                 } else {
616                     if (BluetoothStackWIDCOMMSDPInputStream.debug) {
617                         DebugLog.debug("no data for attribute " + id + " Ox" + Integer.toHexString(id));
618                     }
619                 }
620             } catch (Throwable e) {
621                 if (BluetoothStackWIDCOMMSDPInputStream.debug) {
622                     DebugLog.error("error populate attribute " + id + " Ox" + Integer.toHexString(id), e);
623                 }
624             }
625         }
626         return anyRetrived;
627     }
628 
629     // --- Client RFCOMM connections
630 
631     private native long connectionRfOpenClientConnectionImpl(long address, int channel, boolean authenticate, boolean encrypt, int timeout) throws IOException;
632 
633     public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException {
634         verifyDeviceReady();
635         return connectionRfOpenClientConnectionImpl(params.address, params.channel, params.authenticate, params.encrypt, params.timeout);
636     }
637 
638     private native void closeRfCommPortImpl(long handle) throws IOException;
639 
640     public void connectionRfCloseClientConnection(long handle) throws IOException {
641         closeRfCommPortImpl(handle);
642     }
643 
644     public native long getConnectionRfRemoteAddress(long handle) throws IOException;
645 
646     public native int connectionRfRead(long handle) throws IOException;
647 
648     public native int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException;
649 
650     public native int connectionRfReadAvailable(long handle) throws IOException;
651 
652     private native void connectionRfWriteImpl(long handle, byte[] b, int off, int len) throws IOException;
653 
654     public void connectionRfWrite(long handle, int b) throws IOException {
655         byte buf[] = new byte[1];
656         buf[0] = (byte) (b & 0xFF);
657         connectionRfWriteImpl(handle, buf, 0, 1);
658     }
659 
660     public void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException {
661         // Max in WIDCOMM is 64K that will cause ACCESS_VIOLATION.
662         final int maxNativeBuffer = 0x10000 - 1;
663         if (len < maxNativeBuffer) {
664             connectionRfWriteImpl(handle, b, off, len);
665         } else {
666             int done = 0;
667             while (done < len) {
668                 int l = len - done;
669                 if (l > maxNativeBuffer) {
670                     l = maxNativeBuffer;
671                 }
672                 connectionRfWriteImpl(handle, b, off + done, l);
673                 done += maxNativeBuffer;
674             }
675         }
676     }
677 
678     public void connectionRfFlush(long handle) throws IOException {
679         // TODO are there any flush
680     }
681 
682     public int rfGetSecurityOpt(long handle, int expected) throws IOException {
683         return expected;
684     }
685 
686     /*
687      * (non-Javadoc)
688      * 
689      * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
690      */
691     public boolean rfEncrypt(long address, long handle, boolean on) throws IOException {
692         return false;
693     }
694 
695     private native synchronized long rfServerOpenImpl(byte[] uuidValue, byte[] uuidValue2, boolean obexSrv, String name, boolean authenticate, boolean encrypt)
696             throws IOException;
697 
698     private native int rfServerSCN(long handle) throws IOException;
699 
700     public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord) throws IOException {
701         verifyDeviceReady();
702         byte[] uuidValue = Utils.UUIDToByteArray(params.uuid);
703         byte[] uuidValue2 = params.obex ? null : Utils.UUIDToByteArray(BluetoothConsts.SERIAL_PORT_UUID);
704         long handle = rfServerOpenImpl(uuidValue, uuidValue2, params.obex, params.name, params.authenticate, params.encrypt);
705         int channel = rfServerSCN(handle);
706         DebugLog.debug("serverSCN", channel);
707         long serviceRecordHandle = handle;
708 
709         serviceRecord.populateRFCOMMAttributes(serviceRecordHandle, channel, params.uuid, params.name, params.obex);
710 
711         return handle;
712     }
713 
714     private native void sdpServiceAddAttribute(long handle, char handleType, int attrID, short attrType, byte[] value) throws ServiceRegistrationException;
715 
716     private byte[] long2byte(long value, int len) {
717         byte[] cvalue = new byte[len];
718         long l = value;
719         for (int i = len - 1; i >= 0; i--) {
720             cvalue[i] = (byte) (l & 0xFF);
721             l >>= 8;
722         }
723         return cvalue;
724     }
725 
726     public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException {
727         sdpServiceUpdateServiceRecord(handle, 'r', serviceRecord);
728     }
729 
730     private byte[] sdpServiceSequenceAttribute(Enumeration en) throws ServiceRegistrationException {
731         ByteArrayOutputStream out = new ByteArrayOutputStream();
732         SDPOutputStream sdpOut = new SDPOutputStream(out);
733         try {
734             while (en.hasMoreElements()) {
735                 sdpOut.writeElement((DataElement) en.nextElement());
736             }
737         } catch (IOException e) {
738             throw new ServiceRegistrationException(e.getMessage());
739         }
740         return out.toByteArray();
741     }
742 
743     private native void sdpServiceAddServiceClassIdList(long handle, char handleType, byte[][] uuidValues) throws ServiceRegistrationException;
744 
745     private void sdpServiceUpdateServiceRecord(long handle, char handleType, ServiceRecordImpl serviceRecord) throws ServiceRegistrationException {
746         int[] ids = serviceRecord.getAttributeIDs();
747         if ((ids == null) || (ids.length == 0)) {
748             return;
749         }
750         // Update the records that can be update only once
751         DataElement serviceClassIDList = serviceRecord.getAttributeValue(BluetoothConsts.ServiceClassIDList);
752         if (serviceClassIDList.getDataType() != DataElement.DATSEQ) {
753             throw new ServiceRegistrationException("Invalid serviceClassIDList");
754         }
755         Enumeration en = (Enumeration) serviceClassIDList.getValue();
756         Vector uuids = new Vector();
757         while (en.hasMoreElements()) {
758             DataElement u = (DataElement) en.nextElement();
759             if (u.getDataType() != DataElement.UUID) {
760                 throw new ServiceRegistrationException("Invalid serviceClassIDList element " + u);
761             }
762             uuids.add(u.getValue());
763         }
764         if (uuids.size() > 0) {
765             byte[][] uuidValues = new byte[uuids.size()][];
766             for (int u = 0; u < uuidValues.length; u++) {
767                 uuidValues[u] = Utils.UUIDToByteArray((UUID) uuids.elementAt(u));
768             }
769             sdpServiceAddServiceClassIdList(handle, handleType, uuidValues);
770         }
771 
772         // Update all other records
773         for (int i = 0; i < ids.length; i++) {
774             int id = ids[i];
775             switch (id) {
776             case BluetoothConsts.ServiceRecordHandle:
777             case BluetoothConsts.ServiceClassIDList:
778             case BluetoothConsts.ProtocolDescriptorList:
779             case BluetoothConsts.AttributeIDServiceName:
780                 continue;
781             }
782 
783             DataElement d = serviceRecord.getAttributeValue(id);
784             switch (d.getDataType()) {
785             case DataElement.U_INT_1:
786                 sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, long2byte(d.getLong(), 1));
787                 break;
788             case DataElement.U_INT_2:
789                 sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, long2byte(d.getLong(), 2));
790                 break;
791             case DataElement.U_INT_4:
792                 sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, long2byte(d.getLong(), 4));
793                 break;
794             case DataElement.U_INT_8:
795             case DataElement.U_INT_16:
796                 sdpServiceAddAttribute(handle, handleType, id, UINT_DESC_TYPE, (byte[]) d.getValue());
797                 break;
798             case DataElement.INT_1:
799                 sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 1));
800                 break;
801             case DataElement.INT_2:
802                 sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 2));
803                 break;
804             case DataElement.INT_4:
805                 sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 4));
806                 break;
807             case DataElement.INT_8:
808                 sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, long2byte(d.getLong(), 8));
809                 break;
810             case DataElement.INT_16:
811                 sdpServiceAddAttribute(handle, handleType, id, TWO_COMP_INT_DESC_TYPE, (byte[]) d.getValue());
812                 break;
813             case DataElement.URL:
814                 sdpServiceAddAttribute(handle, handleType, id, URL_DESC_TYPE, Utils.getASCIIBytes(d.getValue().toString()));
815                 break;
816             case DataElement.STRING:
817                 sdpServiceAddAttribute(handle, handleType, id, TEXT_STR_DESC_TYPE, Utils.getUTF8Bytes(d.getValue().toString()));
818                 break;
819             case DataElement.NULL:
820                 sdpServiceAddAttribute(handle, handleType, id, NULL_DESC_TYPE, null);
821                 break;
822             case DataElement.BOOL:
823                 sdpServiceAddAttribute(handle, handleType, id, BOOLEAN_DESC_TYPE, new byte[] { (byte) (d.getBoolean() ? 1 : 0) });
824                 break;
825             case DataElement.UUID:
826                 sdpServiceAddAttribute(handle, handleType, id, UUID_DESC_TYPE, BluetoothStackWIDCOMMSDPInputStream.getUUIDHexBytes((UUID) d.getValue()));
827                 break;
828             case DataElement.DATSEQ:
829                 sdpServiceAddAttribute(handle, handleType, id, DATA_ELE_SEQ_DESC_TYPE, sdpServiceSequenceAttribute((Enumeration) d.getValue()));
830                 break;
831             case DataElement.DATALT:
832                 sdpServiceAddAttribute(handle, handleType, id, DATA_ELE_ALT_DESC_TYPE, sdpServiceSequenceAttribute((Enumeration) d.getValue()));
833                 break;
834             default:
835                 throw new ServiceRegistrationException("Invalid " + d.getDataType());
836             }
837         }
838     }
839 
840     public native long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException;
841 
842     public native void connectionRfCloseServerConnection(long handle) throws IOException;
843 
844     private native void rfServerCloseImpl(long handle) throws IOException;
845 
846     public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
847         rfServerCloseImpl(handle);
848     }
849 
850     // ---------------------- Client and Server L2CAP connections
851 
852     private void validateMTU(int receiveMTU, int transmitMTU) {
853         if (receiveMTU > RECEIVE_MTU_MAX) {
854             throw new IllegalArgumentException("invalid ReceiveMTU value " + receiveMTU);
855         }
856         // if (transmitMTU > RECEIVE_MTU_MAX) {
857         // throw new IllegalArgumentException("invalid TransmitMTU value " +
858         // transmitMTU);
859         // }
860         // int min = L2CAPConnection.DEFAULT_MTU;
861         // if ((receiveMTU > L2CAPConnection.MINIMUM_MTU) && (receiveMTU < min))
862         // {
863         // min = receiveMTU;
864         // }
865         // if ((transmitMTU > L2CAPConnection.MINIMUM_MTU) && (transmitMTU <
866         // min)) {
867         // min = transmitMTU;
868         // }
869         // return min;
870     }
871 
872     private native long l2OpenClientConnectionImpl(long address, int channel, boolean authenticate, boolean encrypt, int receiveMTU, int transmitMTU,
873             int timeout) throws IOException;
874 
875     /*
876      * (non-Javadoc)
877      * 
878      * @see
879      * com.intel.bluetooth.BluetoothStack#l2OpenClientConnection(com.intel.bluetooth
880      * .BluetoothConnectionParams, int, int)
881      */
882     public long l2OpenClientConnection(BluetoothConnectionParams params, int receiveMTU, int transmitMTU) throws IOException {
883         verifyDeviceReady();
884         validateMTU(receiveMTU, transmitMTU);
885         return l2OpenClientConnectionImpl(params.address, params.channel, params.authenticate, params.encrypt, receiveMTU, transmitMTU, params.timeout);
886     }
887 
888     /*
889      * (non-Javadoc)
890      * 
891      * @see com.intel.bluetooth.BluetoothStack#l2CloseClientConnection(long)
892      */
893     public native void l2CloseClientConnection(long handle) throws IOException;
894 
895     private native synchronized long l2ServerOpenImpl(byte[] uuidValue, boolean authenticate, boolean encrypt, String name, int receiveMTU, int transmitMTU,
896             int assignPsm) throws IOException;
897 
898     public native int l2ServerPSM(long handle) throws IOException;
899 
900     /*
901      * (non-Javadoc)
902      * 
903      * @seecom.intel.bluetooth.BluetoothStack#l2ServerOpen(com.intel.bluetooth.
904      * BluetoothConnectionNotifierParams, int, int,
905      * com.intel.bluetooth.ServiceRecordImpl)
906      */
907     public long l2ServerOpen(BluetoothConnectionNotifierParams params, int receiveMTU, int transmitMTU, ServiceRecordImpl serviceRecord) throws IOException {
908         verifyDeviceReady();
909         validateMTU(receiveMTU, transmitMTU);
910         byte[] uuidValue = Utils.UUIDToByteArray(params.uuid);
911         long handle = l2ServerOpenImpl(uuidValue, params.authenticate, params.encrypt, params.name, receiveMTU, transmitMTU, params.bluecove_ext_psm);
912 
913         int channel = l2ServerPSM(handle);
914 
915         int serviceRecordHandle = (int) handle;
916 
917         serviceRecord.populateL2CAPAttributes(serviceRecordHandle, channel, params.uuid, params.name);
918 
919         return handle;
920     }
921 
922     /*
923      * (non-Javadoc)
924      * 
925      * @see com.intel.bluetooth.BluetoothStack#l2ServerUpdateServiceRecord(long,
926      * com.intel.bluetooth.ServiceRecordImpl, boolean)
927      */
928     public void l2ServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException {
929         sdpServiceUpdateServiceRecord(handle, 'l', serviceRecord);
930     }
931 
932     /*
933      * (non-Javadoc)
934      * 
935      * @see
936      * com.intel.bluetooth.BluetoothStack#l2ServerAcceptAndOpenServerConnection
937      * (long)
938      */
939     public native long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException;
940 
941     /*
942      * (non-Javadoc)
943      * 
944      * @see com.intel.bluetooth.BluetoothStack#l2CloseServerConnection(long)
945      */
946     public native void l2CloseServerConnection(long handle) throws IOException;
947 
948     private native void l2ServerCloseImpl(long handle) throws IOException;
949 
950     /*
951      * (non-Javadoc)
952      * 
953      * @see com.intel.bluetooth.BluetoothStack#l2ServerClose(long,
954      * com.intel.bluetooth.ServiceRecordImpl)
955      */
956     public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
957         l2ServerCloseImpl(handle);
958     }
959 
960     /*
961      * (non-Javadoc)
962      * 
963      * @see com.intel.bluetooth.BluetoothStack#l2GetSecurityOpt(long, int)
964      */
965     public int l2GetSecurityOpt(long handle, int expected) throws IOException {
966         return expected;
967     }
968 
969     /*
970      * (non-Javadoc)
971      * 
972      * @see com.intel.bluetooth.BluetoothStack#l2GetReceiveMTU(long)
973      */
974     public native int l2GetReceiveMTU(long handle) throws IOException;
975 
976     /*
977      * (non-Javadoc)
978      * 
979      * @see com.intel.bluetooth.BluetoothStack#l2GetTransmitMTU(long)
980      */
981     public native int l2GetTransmitMTU(long handle) throws IOException;
982 
983     /*
984      * (non-Javadoc)
985      * 
986      * @see com.intel.bluetooth.BluetoothStack#l2Ready(long)
987      */
988     public native boolean l2Ready(long handle) throws IOException;
989 
990     /*
991      * (non-Javadoc)
992      * 
993      * @see com.intel.bluetooth.BluetoothStack#l2receive(long, byte[])
994      */
995     public native int l2Receive(long handle, byte[] inBuf) throws IOException;
996 
997     /*
998      * (non-Javadoc)
999      * 
1000      * @see com.intel.bluetooth.BluetoothStack#l2send(long, byte[])
1001      */
1002     public native void l2Send(long handle, byte[] data) throws IOException;
1003 
1004     /*
1005      * (non-Javadoc)
1006      * 
1007      * @see com.intel.bluetooth.BluetoothStack#l2RemoteAddress(long)
1008      */
1009     public native long l2RemoteAddress(long handle) throws IOException;
1010 
1011     /*
1012      * (non-Javadoc)
1013      * 
1014      * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
1015      */
1016     public boolean l2Encrypt(long address, long handle, boolean on) throws IOException {
1017         return false;
1018     }
1019 }