View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2007 Eric Wagner
4    *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
5    *
6    *  Licensed to the Apache Software Foundation (ASF) under one
7    *  or more contributor license agreements.  See the NOTICE file
8    *  distributed with this work for additional information
9    *  regarding copyright ownership.  The ASF licenses this file
10   *  to you under the Apache License, Version 2.0 (the
11   *  "License"); you may not use this file except in compliance
12   *  with the License.  You may obtain a copy of the License at
13   *
14   *    http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing,
17   *  software distributed under the License is distributed on an
18   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19   *  KIND, either express or implied.  See the License for the
20   *  specific language governing permissions and limitations
21   *  under the License.
22   *
23   *  @version $Id: BluetoothStackOSX.java 2557 2008-12-11 08:19:30Z skarzhevskyy $
24   */
25  package com.intel.bluetooth;
26  
27  import java.io.ByteArrayInputStream;
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.BluetoothConnectionException;
34  import javax.bluetooth.BluetoothStateException;
35  import javax.bluetooth.DataElement;
36  import javax.bluetooth.DeviceClass;
37  import javax.bluetooth.DiscoveryAgent;
38  import javax.bluetooth.DiscoveryListener;
39  import javax.bluetooth.RemoteDevice;
40  import javax.bluetooth.ServiceRecord;
41  import javax.bluetooth.ServiceRegistrationException;
42  import javax.bluetooth.UUID;
43  
44  class BluetoothStackOSX implements BluetoothStack {
45  
46      public static final boolean debug = false;
47  
48      private static BluetoothStackOSX singleInstance = null;
49  
50      // TODO what is the real number for Attributes retrivable ?
51      private final static int ATTR_RETRIEVABLE_MAX = 256;
52  
53      private Vector deviceDiscoveryListeners = new Vector/* <DiscoveryListener> */();
54  
55      private Hashtable deviceDiscoveryListenerReportedDevices = new Hashtable();
56  
57      private int receive_mtu_max = -1;
58  
59      private int localDeviceSupportedSoftwareVersion;
60  
61      private long lastDeviceDiscoveryTime = 0;
62  
63      private int localDeviceServiceClasses = 0;
64  
65      private Thread localDeviceServiceClassMaintainer = null;
66  
67      private static final int BLUETOOTH_SOFTWARE_VERSION_2_0_0 = 20000;
68  
69      BluetoothStackOSX() {
70  
71      }
72  
73      // ---------------------- Library initialization
74  
75      /*
76       * (non-Javadoc)
77       * 
78       * @see com.intel.bluetooth.BluetoothStack#isNativeCodeLoaded()
79       */
80      public native boolean isNativeCodeLoaded();
81  
82      /*
83       * (non-Javadoc)
84       * 
85       * @see com.intel.bluetooth.BluetoothStack#requireNativeLibraries()
86       */
87      public LibraryInformation[] requireNativeLibraries() {
88          return LibraryInformation.library(BlueCoveImpl.NATIVE_LIB_OSX);
89      }
90  
91      public String getStackID() {
92          return BlueCoveImpl.STACK_OSX;
93      }
94  
95      public String toString() {
96          return getStackID();
97      }
98  
99      /*
100      * (non-Javadoc)
101      * 
102      * @see com.intel.bluetooth.BluetoothStack#getFeatureSet()
103      */
104     public int getFeatureSet() {
105         if (localDeviceSupportedSoftwareVersion >= BLUETOOTH_SOFTWARE_VERSION_2_0_0) {
106             return FEATURE_L2CAP | FEATURE_SERVICE_ATTRIBUTES | FEATURE_SET_DEVICE_SERVICE_CLASSES;
107         } else {
108             return FEATURE_L2CAP | FEATURE_SERVICE_ATTRIBUTES;
109         }
110     }
111 
112     public native int getLibraryVersion();
113 
114     public native int detectBluetoothStack();
115 
116     private native boolean initializeImpl();
117 
118     public void initialize() throws BluetoothStateException {
119         if (singleInstance != null) {
120             throw new BluetoothStateException("Only one instance of " + getStackID() + " stack supported");
121         }
122         localDeviceSupportedSoftwareVersion = getLocalDeviceSupportedSoftwareVersion();
123         DebugLog.debug("localDeviceSupportedSoftwareVersion", localDeviceSupportedSoftwareVersion);
124         if (!initializeImpl()) {
125             throw new BluetoothStateException("OS X BluetoothStack not found");
126         }
127         singleInstance = this;
128     }
129 
130     public void destroy() {
131         if (localDeviceSupportedSoftwareVersion >= BLUETOOTH_SOFTWARE_VERSION_2_0_0) {
132             setLocalDeviceServiceClassesImpl(0);
133         }
134         singleInstance = null;
135     }
136 
137     public native void enableNativeDebug(Class nativeDebugCallback, boolean on);
138 
139     /*
140      * (non-Javadoc)
141      * 
142      * @see
143      * com.intel.bluetooth.BluetoothStack#isCurrentThreadInterruptedCallback()
144      */
145     public boolean isCurrentThreadInterruptedCallback() {
146         return UtilsJavaSE.isCurrentThreadInterrupted();
147     }
148 
149     // ---------------------- LocalDevice
150 
151     public native String getLocalDeviceBluetoothAddress() throws BluetoothStateException;
152 
153     public native String getLocalDeviceName();
154 
155     private native int getDeviceClassImpl();
156 
157     public DeviceClass getLocalDeviceClass() {
158         return new DeviceClass(getDeviceClassImpl());
159     }
160 
161     private native boolean setLocalDeviceServiceClassesImpl(int classOfDevice);
162 
163     private class MaintainDeviceServiceClassesThread extends Thread {
164 
165         MaintainDeviceServiceClassesThread() {
166             super("MaintainDeviceServiceClassesThread");
167         }
168 
169         public void run() {
170             boolean updated = true;
171             while (true) {
172                 try {
173                     int delay = 1000 * 120;
174                     if (!updated) {
175                         delay = 1000;
176                     }
177                     Thread.sleep(delay);
178                 } catch (InterruptedException e) {
179                     break;
180                 }
181                 if (localDeviceServiceClasses != 0) {
182                     updated = setLocalDeviceServiceClassesImpl(localDeviceServiceClasses);
183                 } else if (!updated) {
184                     updated = true;
185                 }
186             }
187         }
188     }
189 
190     /*
191      * (non-Javadoc)
192      * 
193      * @see com.intel.bluetooth.BluetoothStack#setLocalDeviceServiceClasses(int)
194      */
195     public synchronized void setLocalDeviceServiceClasses(int classOfDevice) {
196         if (localDeviceSupportedSoftwareVersion < BLUETOOTH_SOFTWARE_VERSION_2_0_0) {
197             return;
198         }
199         if (classOfDevice != localDeviceServiceClasses) {
200             setLocalDeviceServiceClassesImpl(classOfDevice);
201         }
202         localDeviceServiceClasses = classOfDevice;
203         if ((classOfDevice != 0) && (localDeviceServiceClassMaintainer == null)) {
204             localDeviceServiceClassMaintainer = new MaintainDeviceServiceClassesThread();
205             UtilsJavaSE.threadSetDaemon(localDeviceServiceClassMaintainer);
206             localDeviceServiceClassMaintainer.start();
207         }
208     }
209 
210     public native boolean isLocalDevicePowerOn();
211 
212     private native boolean isLocalDeviceFeatureSwitchRoles();
213 
214     private native boolean isLocalDeviceFeatureParkMode();
215 
216     private native int getLocalDeviceL2CAPMTUMaximum();
217 
218     private native int getLocalDeviceSupportedSoftwareVersion();
219 
220     private native String getLocalDeviceSoftwareVersionInfo();
221 
222     private native int getLocalDeviceManufacturer();
223 
224     private native String getLocalDeviceVersion();
225 
226     public String getLocalDeviceProperty(String property) {
227         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_DEVICES_MAX.equals(property)) {
228             return isLocalDeviceFeatureParkMode() ? "255" : "7";
229         }
230         if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_TRANS_MAX.equals(property)) {
231             return "7";
232         }
233         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY_SCAN.equals(property)) {
234             return BlueCoveImpl.TRUE;
235         }
236         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE_SCAN.equals(property)) {
237             return BlueCoveImpl.TRUE;
238         }
239         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_INQUIRY.equals(property)) {
240             return BlueCoveImpl.TRUE;
241         }
242         if (BluetoothConsts.PROPERTY_BLUETOOTH_CONNECTED_PAGE.equals(property)) {
243             return BlueCoveImpl.TRUE;
244         }
245 
246         if (BluetoothConsts.PROPERTY_BLUETOOTH_SD_ATTR_RETRIEVABLE_MAX.equals(property)) {
247             return String.valueOf(ATTR_RETRIEVABLE_MAX);
248         }
249         if (BluetoothConsts.PROPERTY_BLUETOOTH_MASTER_SWITCH.equals(property)) {
250             // return isLocalDeviceFeatureSwitchRoles() ? TRUE : FALSE;
251             return BlueCoveImpl.FALSE;
252         }
253         if (BluetoothConsts.PROPERTY_BLUETOOTH_L2CAP_RECEIVEMTU_MAX.equals(property)) {
254             return String.valueOf(receiveMTUMAX());
255         }
256 
257         if ("bluecove.radio.version".equals(property)) {
258             return getLocalDeviceVersion();
259         }
260         if ("bluecove.radio.manufacturer".equals(property)) {
261             return String.valueOf(getLocalDeviceManufacturer());
262         }
263         if ("bluecove.stack.version".equals(property)) {
264             return getLocalDeviceSoftwareVersionInfo();
265         }
266 
267         return null;
268     }
269 
270     private int receiveMTUMAX() {
271         if (receive_mtu_max < 0) {
272             receive_mtu_max = getLocalDeviceL2CAPMTUMaximum();
273         }
274         return receive_mtu_max;
275     }
276 
277     private native boolean getLocalDeviceDiscoverableImpl();
278 
279     public int getLocalDeviceDiscoverable() {
280         if (getLocalDeviceDiscoverableImpl()) {
281             return DiscoveryAgent.GIAC;
282         } else {
283             return DiscoveryAgent.NOT_DISCOVERABLE;
284         }
285     }
286 
287     /**
288      * There are no functions to set OS X stack Discoverable status.
289      */
290     public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException {
291         if (getLocalDeviceDiscoverable() == mode) {
292             return true;
293         }
294         return false;
295     }
296 
297     private void verifyDeviceReady() throws BluetoothStateException {
298         if (!isLocalDevicePowerOn()) {
299             throw new BluetoothStateException("Bluetooth Device is not ready");
300         }
301     }
302 
303     public RemoteDevice[] retrieveDevices(int option) {
304         return null;
305     }
306 
307     public Boolean isRemoteDeviceTrusted(long address) {
308         return null;
309     }
310 
311     public Boolean isRemoteDeviceAuthenticated(long address) {
312         return null;
313     }
314 
315     // ---------------------- Remote Device authentication
316 
317     public boolean authenticateRemoteDevice(long address) throws IOException {
318         return false;
319     }
320 
321     /*
322      * (non-Javadoc)
323      * 
324      * @see com.intel.bluetooth.BluetoothStack#authenticateRemoteDevice(long,
325      * java.lang.String)
326      */
327     public boolean authenticateRemoteDevice(long address, String passkey) throws IOException {
328         return false;
329     }
330 
331     /*
332      * (non-Javadoc)
333      * 
334      * @see
335      * com.intel.bluetooth.BluetoothStack#removeAuthenticationWithRemoteDevice
336      * (long)
337      */
338     public void removeAuthenticationWithRemoteDevice(long address) throws IOException {
339         throw new NotSupportedIOException(getStackID());
340     }
341 
342     // ---------------------- Device Inquiry
343 
344     public native String getRemoteDeviceFriendlyName(long address) throws IOException;
345 
346     private native int runDeviceInquiryImpl(DeviceInquiryRunnable inquiryRunnable, DeviceInquiryThread startedNotify, int accessCode, int duration, DiscoveryListener listener)
347             throws BluetoothStateException;
348 
349     public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException {
350         // Inquiries are throttled if they are called too quickly in succession.
351         // e.g. JSR-82 TCK
352         long sinceDiscoveryLast = System.currentTimeMillis() - lastDeviceDiscoveryTime;
353         long acceptableInterval = 7 * 1000;
354         if (sinceDiscoveryLast < acceptableInterval) {
355             try {
356                 Thread.sleep(acceptableInterval - sinceDiscoveryLast);
357             } catch (InterruptedException e) {
358                 throw new BluetoothStateException();
359             }
360         }
361 
362         deviceDiscoveryListeners.addElement(listener);
363         deviceDiscoveryListenerReportedDevices.put(listener, new Vector());
364         DeviceInquiryRunnable inquiryRunnable = new DeviceInquiryRunnable() {
365 
366             public int runDeviceInquiry(DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener) throws BluetoothStateException {
367                 try {
368                     return runDeviceInquiryImpl(this, startedNotify, accessCode, DeviceInquiryThread.getConfigDeviceInquiryDuration(), listener);
369                 } finally {
370                     lastDeviceDiscoveryTime = System.currentTimeMillis();
371                     deviceDiscoveryListeners.removeElement(listener);
372                     deviceDiscoveryListenerReportedDevices.remove(listener);
373                 }
374             }
375 
376             public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass, String deviceName, boolean paired) {
377                 if (!deviceDiscoveryListeners.contains(listener)) {
378                     return;
379                 }
380                 // Update name if name retrieved
381                 RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(BluetoothStackOSX.this, deviceAddr, deviceName, paired);
382                 Vector reported = (Vector) deviceDiscoveryListenerReportedDevices.get(listener);
383                 if (reported == null || (reported.contains(remoteDevice))) {
384                     return;
385                 }
386                 reported.addElement(remoteDevice);
387                 DeviceClass cod = new DeviceClass(deviceClass);
388                 DebugLog.debug("deviceDiscoveredCallback address", remoteDevice.getBluetoothAddress());
389                 DebugLog.debug("deviceDiscoveredCallback deviceClass", cod);
390                 listener.deviceDiscovered(remoteDevice, cod);
391             }
392         };
393         return DeviceInquiryThread.startInquiry(this, inquiryRunnable, accessCode, listener);
394     }
395 
396     private native boolean deviceInquiryCancelImpl();
397 
398     public boolean cancelInquiry(DiscoveryListener listener) {
399         // no further deviceDiscovered() events will occur for this inquiry
400         if (!deviceDiscoveryListeners.removeElement(listener)) {
401             return false;
402         }
403         return deviceInquiryCancelImpl();
404     }
405 
406     // ---------------------- Service search
407 
408     private native int runSearchServicesImpl(long address, int transID) throws BluetoothStateException, SearchServicesException;
409 
410     public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException {
411 
412         SearchServicesRunnable searchRunnable = new SearchServicesRunnable() {
413 
414             public int runSearchServices(SearchServicesThread sst, int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
415                     throws BluetoothStateException {
416                 // OS X will retrieve all Records, we filter here in Java
417                 sst.searchServicesStartedCallback();
418                 int recordsSize;
419                 try {
420                     recordsSize = runSearchServicesImpl(RemoteDeviceHelper.getAddress(device), sst.getTransID());
421                 } catch (SearchServicesDeviceNotReachableException e) {
422                     return DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE;
423                 } catch (SearchServicesTerminatedException e) {
424                     return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
425                 } catch (SearchServicesException e) {
426                     return DiscoveryListener.SERVICE_SEARCH_ERROR;
427                 }
428                 if (sst.isTerminated()) {
429                     return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
430                 }
431                 if (recordsSize == 0) {
432                     return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS;
433                 }
434                 Vector records = new Vector();
435                 int[] uuidFilerAttrIDs = new int[] { BluetoothConsts.ServiceClassIDList, BluetoothConsts.ProtocolDescriptorList };
436                 int[] requiredAttrIDs = new int[] { BluetoothConsts.ServiceRecordHandle, BluetoothConsts.ServiceRecordState, BluetoothConsts.ServiceID };
437                 nextRecord: for (int i = 0; i < recordsSize; i++) {
438                     ServiceRecordImpl sr = new ServiceRecordImpl(BluetoothStackOSX.this, device, i);
439                     try {
440                         sr.populateRecord(uuidFilerAttrIDs);
441                         // Apply JSR-82 filter, all UUID should be present
442                         for (int u = 0; u < uuidSet.length; u++) {
443                             if (!((sr.hasServiceClassUUID(uuidSet[u]) || sr.hasProtocolClassUUID(uuidSet[u])))) {
444                                 if (debug) {
445                                     DebugLog.debug("filtered ServiceRecord (" + i + ")", sr);
446                                 }
447                                 continue nextRecord;
448                             }
449                         }
450                         if (debug) {
451                             DebugLog.debug("accepted ServiceRecord (" + i + ")", sr);
452                         }
453                         records.addElement(sr);
454                         sr.populateRecord(requiredAttrIDs);
455                         if (attrSet != null) {
456                             sr.populateRecord(attrSet);
457                         }
458                         DebugLog.debug("ServiceRecord (" + i + ")", sr);
459                     } catch (Exception e) {
460                         DebugLog.debug("populateRecord error", e);
461                     }
462 
463                     if (sst.isTerminated()) {
464                         DebugLog.debug("SERVICE_SEARCH_TERMINATED " + sst.getTransID());
465                         return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
466                     }
467                 }
468                 if (records.size() != 0) {
469                     DebugLog.debug("SERVICE_SEARCH_COMPLETED " + sst.getTransID());
470                     ServiceRecord[] fileteredRecords = (ServiceRecord[]) Utils.vector2toArray(records, new ServiceRecord[records.size()]);
471                     listener.servicesDiscovered(sst.getTransID(), fileteredRecords);
472                     return DiscoveryListener.SERVICE_SEARCH_COMPLETED;
473                 } else {
474                     return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS;
475                 }
476             }
477 
478         };
479         return SearchServicesThread.startSearchServices(this, searchRunnable, attrSet, uuidSet, device, listener);
480     }
481 
482     private native void cancelServiceSearchImpl(int transID);
483 
484     public boolean cancelServiceSearch(int transID) {
485         SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID);
486         if (sst != null) {
487             synchronized (this) {
488                 if (!sst.isTerminated()) {
489                     sst.setTerminated();
490                     cancelServiceSearchImpl(transID);
491                     return true;
492                 }
493             }
494         }
495         return false;
496     }
497 
498     private native byte[] getServiceAttributeImpl(long address, long serviceRecordIndex, int attrID);
499 
500     public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs) throws IOException {
501         if (attrIDs.length > ATTR_RETRIEVABLE_MAX) {
502             throw new IllegalArgumentException();
503         }
504         boolean anyRetrived = false;
505         long address = RemoteDeviceHelper.getAddress(serviceRecord.getHostDevice());
506         for (int i = 0; i < attrIDs.length; i++) {
507             int id = attrIDs[i];
508             try {
509                 byte[] blob = getServiceAttributeImpl(address, serviceRecord.getHandle(), id);
510                 if (blob != null) {
511                     DataElement element = (new SDPInputStream(new ByteArrayInputStream(blob))).readElement();
512                     serviceRecord.populateAttributeValue(id, element);
513                     anyRetrived = true;
514                     if (debug) {
515                         DebugLog.debug("data for attribute " + id + " Ox" + Integer.toHexString(id) + " " + element);
516                     }
517                 } else {
518                     if (debug) {
519                         DebugLog.debug("no data for attribute " + id + " Ox" + Integer.toHexString(id));
520                     }
521                 }
522             } catch (Throwable e) {
523                 if (debug) {
524                     DebugLog.error("error populate attribute " + id + " Ox" + Integer.toHexString(id), e);
525                 }
526             }
527         }
528         return anyRetrived;
529     }
530 
531     // ---------------------- Client RFCOMM connections
532 
533     private native long connectionRfOpenClientConnectionImpl(long address, int channel, boolean authenticate, boolean encrypt, int timeout) throws IOException;
534 
535     public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException {
536         if (params.encrypt) {
537             throw new BluetoothConnectionException(BluetoothConnectionException.SECURITY_BLOCK, "encrypt mode not supported");
538         }
539         Object lock = RemoteDeviceHelper.createRemoteDevice(this, params.address, null, false);
540         synchronized (lock) {
541             return connectionRfOpenClientConnectionImpl(params.address, params.channel, params.authenticate, params.encrypt, params.timeout);
542         }
543     }
544 
545     public native void connectionRfCloseClientConnection(long handle) throws IOException;
546 
547     public native int rfGetSecurityOpt(long handle, int expected) throws IOException;
548 
549     /*
550      * (non-Javadoc)
551      * 
552      * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
553      */
554     public boolean rfEncrypt(long address, long handle, boolean on) throws IOException {
555         return false;
556     }
557 
558     // ---------------------- Server RFCOMM connections
559 
560     private native long rfServerCreateImpl(byte[] uuidValue, boolean obexSrv, String name, boolean authenticate, boolean encrypt) throws IOException;
561 
562     private native int rfServerGetChannelID(long handle) throws IOException;
563 
564     private native void rfServerCloseImpl(long handle) throws IOException;
565 
566     public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord) throws IOException {
567         verifyDeviceReady();
568         if (params.encrypt) {
569             throw new BluetoothConnectionException(BluetoothConnectionException.SECURITY_BLOCK, "encrypt mode not supported");
570         }
571         byte[] uuidValue = Utils.UUIDToByteArray(params.uuid);
572         long handle = rfServerCreateImpl(uuidValue, params.obex, params.name, params.authenticate, params.encrypt);
573         boolean success = false;
574         try {
575             int channel = rfServerGetChannelID(handle);
576             serviceRecord.populateRFCOMMAttributes(handle, channel, params.uuid, params.name, params.obex);
577             success = true;
578         } finally {
579             if (!success) {
580                 rfServerCloseImpl(handle);
581             }
582         }
583         return handle;
584     }
585 
586     public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
587         rfServerCloseImpl(handle);
588     }
589 
590     public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException {
591         sdpServiceUpdateServiceRecord(handle, 'R', serviceRecord);
592     }
593 
594     public native long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException;
595 
596     public void connectionRfCloseServerConnection(long handle) throws IOException {
597         connectionRfCloseClientConnection(handle);
598     }
599 
600     private native void sdpServiceUpdateServiceRecordPublish(long handle, char handleType) throws ServiceRegistrationException;
601 
602     private native void sdpServiceAddAttribute(long handle, char handleType, int attrID, int attrType, long numberValue, byte[] arrayValue)
603             throws ServiceRegistrationException;
604 
605     private native void sdpServiceSequenceAttributeStart(long handle, char handleType, int attrID, int attrType) throws ServiceRegistrationException;
606 
607     private native void sdpServiceSequenceAttributeEnd(long handle, char handleType, int attrID) throws ServiceRegistrationException;
608 
609     private void sdpServiceAddAttribute(long handle, char handleType, int attrID, DataElement element) throws ServiceRegistrationException {
610         int type = element.getDataType();
611         switch (type) {
612         case DataElement.NULL:
613             sdpServiceAddAttribute(handle, handleType, attrID, type, 0, null);
614             break;
615         case DataElement.BOOL:
616             sdpServiceAddAttribute(handle, handleType, attrID, type, element.getBoolean() ? 1 : 0, null);
617             break;
618         case DataElement.U_INT_1:
619         case DataElement.INT_1:
620         case DataElement.U_INT_2:
621         case DataElement.INT_2:
622         case DataElement.U_INT_4:
623         case DataElement.INT_4:
624         case DataElement.INT_8:
625             sdpServiceAddAttribute(handle, handleType, attrID, type, element.getLong(), null);
626             break;
627         case DataElement.U_INT_8:
628         case DataElement.U_INT_16:
629         case DataElement.INT_16:
630             sdpServiceAddAttribute(handle, handleType, attrID, type, 0, (byte[]) element.getValue());
631             break;
632         case DataElement.UUID:
633             sdpServiceAddAttribute(handle, handleType, attrID, type, 0, Utils.UUIDToByteArray((UUID) element.getValue()));
634             break;
635         case DataElement.STRING:
636             byte[] bs = Utils.getUTF8Bytes((String) element.getValue());
637             sdpServiceAddAttribute(handle, handleType, attrID, type, 0, bs);
638             break;
639         case DataElement.URL:
640             byte[] bu = Utils.getASCIIBytes((String) element.getValue());
641             sdpServiceAddAttribute(handle, handleType, attrID, type, 0, bu);
642             break;
643         case DataElement.DATSEQ:
644         case DataElement.DATALT:
645             sdpServiceSequenceAttributeStart(handle, handleType, attrID, type);
646             for (Enumeration e = (Enumeration) element.getValue(); e.hasMoreElements();) {
647                 DataElement child = (DataElement) e.nextElement();
648                 sdpServiceAddAttribute(handle, handleType, -1, child);
649             }
650             sdpServiceSequenceAttributeEnd(handle, handleType, attrID);
651             break;
652         default:
653             throw new IllegalArgumentException();
654         }
655     }
656 
657     private void sdpServiceUpdateServiceRecord(long handle, char handleType, ServiceRecordImpl serviceRecord) throws ServiceRegistrationException {
658         int[] ids = serviceRecord.getAttributeIDs();
659         if ((ids == null) || (ids.length == 0)) {
660             return;
661         }
662         for (int i = 0; i < ids.length; i++) {
663             int attrID = ids[i];
664             switch (attrID) {
665             case BluetoothConsts.ServiceRecordHandle:
666                 continue;
667             case BluetoothConsts.ProtocolDescriptorList:
668             case BluetoothConsts.AttributeIDServiceName:
669                 continue;
670             }
671             sdpServiceAddAttribute(handle, handleType, attrID, serviceRecord.getAttributeValue(attrID));
672         }
673         sdpServiceUpdateServiceRecordPublish(handle, handleType);
674     }
675 
676     // ---------------------- Shared Client and Server RFCOMM connections
677 
678     public void connectionRfFlush(long handle) throws IOException {
679         // TODO Auto-generated method stub
680     }
681 
682     public native int connectionRfRead(long handle) throws IOException;
683 
684     public native int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException;
685 
686     public native int connectionRfReadAvailable(long handle) throws IOException;
687 
688     public void connectionRfWrite(long handle, int b) throws IOException {
689         byte buf[] = new byte[1];
690         buf[0] = (byte) (b & 0xFF);
691         connectionRfWrite(handle, buf, 0, 1);
692     }
693 
694     public native void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException;
695 
696     public native long getConnectionRfRemoteAddress(long handle) throws IOException;
697 
698     // ---------------------- Client and Server L2CAP connections
699 
700     private void validateMTU(int receiveMTU, int transmitMTU) {
701         if (receiveMTU > receiveMTUMAX()) {
702             throw new IllegalArgumentException("invalid ReceiveMTU value " + receiveMTU);
703         }
704     }
705 
706     private native long l2OpenClientConnectionImpl(long address, int channel, boolean authenticate, boolean encrypt, int receiveMTU, int transmitMTU,
707             int timeout) throws IOException;
708 
709     /*
710      * (non-Javadoc)
711      * 
712      * @see
713      * com.intel.bluetooth.BluetoothStack#l2OpenClientConnection(com.intel.bluetooth
714      * .BluetoothConnectionParams, int, int)
715      */
716     public long l2OpenClientConnection(BluetoothConnectionParams params, int receiveMTU, int transmitMTU) throws IOException {
717         validateMTU(receiveMTU, transmitMTU);
718         if (params.encrypt) {
719             throw new BluetoothConnectionException(BluetoothConnectionException.SECURITY_BLOCK, "encrypt mode not supported");
720         }
721         Object lock = RemoteDeviceHelper.createRemoteDevice(this, params.address, null, false);
722         synchronized (lock) {
723             return l2OpenClientConnectionImpl(params.address, params.channel, params.authenticate, params.encrypt, receiveMTU, transmitMTU, params.timeout);
724         }
725     }
726 
727     /*
728      * (non-Javadoc)
729      * 
730      * @see com.intel.bluetooth.BluetoothStack#l2CloseClientConnection(long)
731      */
732     public native void l2CloseClientConnection(long handle) throws IOException;
733 
734     private native long l2ServerOpenImpl(byte[] uuidValue, boolean authenticate, boolean encrypt, String name, int receiveMTU, int transmitMTU, int assignPsm)
735             throws IOException;
736 
737     public native int l2ServerPSM(long handle) throws IOException;
738 
739     /*
740      * (non-Javadoc)
741      * 
742      * @seecom.intel.bluetooth.BluetoothStack#l2ServerOpen(com.intel.bluetooth.
743      * BluetoothConnectionNotifierParams, int, int,
744      * com.intel.bluetooth.ServiceRecordImpl)
745      */
746     public long l2ServerOpen(BluetoothConnectionNotifierParams params, int receiveMTU, int transmitMTU, ServiceRecordImpl serviceRecord) throws IOException {
747         verifyDeviceReady();
748         validateMTU(receiveMTU, transmitMTU);
749         if (params.encrypt) {
750             throw new BluetoothConnectionException(BluetoothConnectionException.SECURITY_BLOCK, "encrypt mode not supported");
751         }
752         byte[] uuidValue = Utils.UUIDToByteArray(params.uuid);
753         long handle = l2ServerOpenImpl(uuidValue, params.authenticate, params.encrypt, params.name, receiveMTU, transmitMTU, params.bluecove_ext_psm);
754 
755         int channel = l2ServerPSM(handle);
756 
757         int serviceRecordHandle = (int) handle;
758 
759         serviceRecord.populateL2CAPAttributes(serviceRecordHandle, channel, params.uuid, params.name);
760 
761         return handle;
762     }
763 
764     /*
765      * (non-Javadoc)
766      * 
767      * @see com.intel.bluetooth.BluetoothStack#l2ServerUpdateServiceRecord(long,
768      * com.intel.bluetooth.ServiceRecordImpl, boolean)
769      */
770     public void l2ServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen) throws ServiceRegistrationException {
771         sdpServiceUpdateServiceRecord(handle, 'L', serviceRecord);
772     }
773 
774     /*
775      * (non-Javadoc)
776      * 
777      * @see
778      * com.intel.bluetooth.BluetoothStack#l2ServerAcceptAndOpenServerConnection
779      * (long)
780      */
781     public native long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException;
782 
783     /*
784      * (non-Javadoc)
785      * 
786      * @see com.intel.bluetooth.BluetoothStack#l2CloseServerConnection(long)
787      */
788     public void l2CloseServerConnection(long handle) throws IOException {
789         l2CloseClientConnection(handle);
790     }
791 
792     private native void l2ServerCloseImpl(long handle) throws IOException;
793 
794     /*
795      * (non-Javadoc)
796      * 
797      * @see com.intel.bluetooth.BluetoothStack#l2ServerClose(long,
798      * com.intel.bluetooth.ServiceRecordImpl)
799      */
800     public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
801         l2ServerCloseImpl(handle);
802     }
803 
804     /*
805      * (non-Javadoc)
806      * 
807      * @see com.intel.bluetooth.BluetoothStack#l2GetSecurityOpt(long, int)
808      */
809     public native int l2GetSecurityOpt(long handle, int expected) throws IOException;
810 
811     /*
812      * (non-Javadoc)
813      * 
814      * @see com.intel.bluetooth.BluetoothStack#l2Ready(long)
815      */
816     public native boolean l2Ready(long handle) throws IOException;
817 
818     /*
819      * (non-Javadoc)
820      * 
821      * @see com.intel.bluetooth.BluetoothStack#l2receive(long, byte[])
822      */
823     public native int l2Receive(long handle, byte[] inBuf) throws IOException;
824 
825     /*
826      * (non-Javadoc)
827      * 
828      * @see com.intel.bluetooth.BluetoothStack#l2send(long, byte[])
829      */
830     public native void l2Send(long handle, byte[] data) throws IOException;
831 
832     /*
833      * (non-Javadoc)
834      * 
835      * @see com.intel.bluetooth.BluetoothStack#l2GetReceiveMTU(long)
836      */
837     public native int l2GetReceiveMTU(long handle) throws IOException;
838 
839     /*
840      * (non-Javadoc)
841      * 
842      * @see com.intel.bluetooth.BluetoothStack#l2GetTransmitMTU(long)
843      */
844     public native int l2GetTransmitMTU(long handle) throws IOException;
845 
846     /*
847      * (non-Javadoc)
848      * 
849      * @see com.intel.bluetooth.BluetoothStack#l2RemoteAddress(long)
850      */
851     public native long l2RemoteAddress(long handle) throws IOException;
852 
853     /*
854      * (non-Javadoc)
855      * 
856      * @see com.intel.bluetooth.BluetoothStack#l2Encrypt(long,long,boolean)
857      */
858     public boolean l2Encrypt(long address, long handle, boolean on) throws IOException {
859         return false;
860     }
861 }