1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.intel.bluetooth;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.util.Enumeration;
26 import java.util.Hashtable;
27
28 import javax.bluetooth.BluetoothStateException;
29 import javax.bluetooth.DataElement;
30 import javax.bluetooth.DeviceClass;
31 import javax.bluetooth.DiscoveryAgent;
32 import javax.bluetooth.DiscoveryListener;
33 import javax.bluetooth.RemoteDevice;
34 import javax.bluetooth.ServiceRecord;
35 import javax.bluetooth.ServiceRegistrationException;
36 import javax.bluetooth.UUID;
37
38 class BluetoothStackMicrosoft implements BluetoothStack, DeviceInquiryRunnable, SearchServicesRunnable {
39
40 private static final int BTH_MODE_POWER_OFF = 1;
41
42 private static final int BTH_MODE_CONNECTABLE = 2;
43
44 private static final int BTH_MODE_DISCOVERABLE = 3;
45
46 private static BluetoothStackMicrosoft singleInstance = null;
47
48 private boolean peerInitialized = false;
49
50 private boolean windowsCE;
51
52 private long localBluetoothAddress = 0;
53
54 private DiscoveryListener currentDeviceDiscoveryListener;
55
56 private Thread limitedDiscoverableTimer;
57
58
59 private final static int ATTR_RETRIEVABLE_MAX = 256;
60
61 private Hashtable deviceDiscoveryDevices;
62
63 BluetoothStackMicrosoft() {
64 }
65
66
67
68 public String getStackID() {
69 return BlueCoveImpl.STACK_WINSOCK;
70 }
71
72
73
74
75
76
77 public native boolean isNativeCodeLoaded();
78
79
80
81
82
83
84 public LibraryInformation[] requireNativeLibraries() {
85 return LibraryInformation.library(BlueCoveImpl.NATIVE_LIB_MS);
86 }
87
88 public String toString() {
89 return getStackID();
90 }
91
92 public native int getLibraryVersion();
93
94 public native int detectBluetoothStack();
95
96 public native void enableNativeDebug(Class nativeDebugCallback, boolean on);
97
98 private static native int initializationStatus() throws IOException;
99
100 private native void uninitialize();
101
102 private native boolean isWindowsCE();
103
104 public void initialize() throws BluetoothStateException {
105 if (singleInstance != null) {
106 throw new BluetoothStateException("Only one instance of " + getStackID() + " stack supported");
107 }
108 try {
109 int status = initializationStatus();
110 DebugLog.debug("initializationStatus", status);
111 if (status == 1) {
112 peerInitialized = true;
113 }
114 windowsCE = isWindowsCE();
115 singleInstance = this;
116 } catch (BluetoothStateException e) {
117 throw e;
118 } catch (IOException e) {
119 DebugLog.fatal("initialization", e);
120 throw new BluetoothStateException(e.getMessage());
121 }
122 }
123
124 public void destroy() {
125 if (singleInstance != this) {
126 throw new RuntimeException("Destroy invalid instance");
127 }
128 if (peerInitialized) {
129 peerInitialized = false;
130 uninitialize();
131 }
132 cancelLimitedDiscoverableTimer();
133 singleInstance = null;
134 }
135
136 private void initialized() throws BluetoothStateException {
137 if (!peerInitialized) {
138 throw new BluetoothStateException("Bluetooth system is unavailable");
139 }
140 }
141
142
143
144
145
146
147 public int getFeatureSet() {
148 return FEATURE_SERVICE_ATTRIBUTES | (windowsCE ? 0 : FEATURE_SET_DEVICE_SERVICE_CLASSES);
149 }
150
151
152
153 private native int getDeviceClass(long address);
154
155 private native void setDiscoverable(boolean on) throws BluetoothStateException;
156
157 private native int getBluetoothRadioMode();
158
159 private native String getradioname(long address);
160
161 private native int getDeviceVersion(long address);
162
163 private native int getDeviceManufacturer(long address);
164
165 public String getLocalDeviceBluetoothAddress() {
166 try {
167 long socket = socket(false, false);
168 bind(socket);
169 localBluetoothAddress = getsockaddress(socket);
170 String address = RemoteDeviceHelper.getBluetoothAddress(localBluetoothAddress);
171 storesockopt(socket);
172 close(socket);
173 return address;
174 } catch (IOException e) {
175 DebugLog.error("get local bluetoothAddress", e);
176 return "000000000000";
177 }
178 }
179
180 public String getLocalDeviceName() {
181 if (localBluetoothAddress == 0) {
182 getLocalDeviceBluetoothAddress();
183 }
184 return getradioname(localBluetoothAddress);
185 }
186
187 public String getRemoteDeviceFriendlyName(long address) throws IOException {
188 return getpeername(address);
189 }
190
191 public DeviceClass getLocalDeviceClass() {
192 return new DeviceClass(getDeviceClass(localBluetoothAddress));
193 }
194
195
196
197
198
199
200 public void setLocalDeviceServiceClasses(int classOfDevice) {
201
202 }
203
204 private void cancelLimitedDiscoverableTimer() {
205 if (limitedDiscoverableTimer != null) {
206 limitedDiscoverableTimer.interrupt();
207 limitedDiscoverableTimer = null;
208 }
209 }
210
211 public boolean setLocalDeviceDiscoverable(int mode) throws BluetoothStateException {
212 switch (mode) {
213 case DiscoveryAgent.NOT_DISCOVERABLE:
214 cancelLimitedDiscoverableTimer();
215 DebugLog.debug("setDiscoverable(false)");
216 setDiscoverable(false);
217 return (DiscoveryAgent.NOT_DISCOVERABLE == getLocalDeviceDiscoverable());
218 case DiscoveryAgent.GIAC:
219 cancelLimitedDiscoverableTimer();
220 DebugLog.debug("setDiscoverable(true)");
221 setDiscoverable(true);
222 return (DiscoveryAgent.GIAC == getLocalDeviceDiscoverable());
223 case DiscoveryAgent.LIAC:
224 cancelLimitedDiscoverableTimer();
225 DebugLog.debug("setDiscoverable(LIAC)");
226 setDiscoverable(true);
227 if (!(DiscoveryAgent.GIAC == getLocalDeviceDiscoverable())) {
228 return false;
229 }
230
231 limitedDiscoverableTimer = Utils.schedule(60 * 1000, new Runnable() {
232 public void run() {
233 try {
234 setDiscoverable(false);
235 } catch (BluetoothStateException e) {
236 DebugLog.debug("error setDiscoverable", e);
237 } finally {
238 limitedDiscoverableTimer = null;
239 }
240 }
241 });
242 return true;
243 }
244 return false;
245 }
246
247 public boolean isLocalDevicePowerOn() {
248 int mode = getBluetoothRadioMode();
249 if (mode == BTH_MODE_POWER_OFF) {
250 return false;
251 }
252 return ((mode == BTH_MODE_CONNECTABLE) || (mode == BTH_MODE_DISCOVERABLE));
253 }
254
255 public int getLocalDeviceDiscoverable() {
256 int mode = getBluetoothRadioMode();
257 if (mode == BTH_MODE_DISCOVERABLE) {
258 if (limitedDiscoverableTimer != null) {
259 DebugLog.debug("Discoverable = LIAC");
260 return DiscoveryAgent.LIAC;
261 } else {
262 DebugLog.debug("Discoverable = GIAC");
263 return DiscoveryAgent.GIAC;
264 }
265 } else {
266 DebugLog.debug("Discoverable = NOT_DISCOVERABLE");
267 return DiscoveryAgent.NOT_DISCOVERABLE;
268 }
269 }
270
271 public String getLocalDeviceProperty(String property) {
272 if ("bluetooth.connected.devices.max".equals(property)) {
273 return "7";
274 }
275 if ("bluetooth.sd.trans.max".equals(property)) {
276 return "7";
277 }
278 if ("bluetooth.connected.inquiry.scan".equals(property)) {
279 return BlueCoveImpl.TRUE;
280 }
281 if ("bluetooth.connected.page.scan".equals(property)) {
282 return BlueCoveImpl.TRUE;
283 }
284 if ("bluetooth.connected.inquiry".equals(property)) {
285 return BlueCoveImpl.TRUE;
286 }
287 if ("bluetooth.connected.page".equals(property)) {
288 return BlueCoveImpl.TRUE;
289 }
290
291 if ("bluetooth.sd.attr.retrievable.max".equals(property)) {
292 return String.valueOf(ATTR_RETRIEVABLE_MAX);
293 }
294 if ("bluetooth.master.switch".equals(property)) {
295 return BlueCoveImpl.FALSE;
296 }
297 if ("bluetooth.l2cap.receiveMTU.max".equals(property)) {
298 return "0";
299 }
300
301 if ("bluecove.radio.version".equals(property)) {
302 return String.valueOf(getDeviceVersion(localBluetoothAddress));
303 }
304 if ("bluecove.radio.manufacturer".equals(property)) {
305 return String.valueOf(getDeviceManufacturer(localBluetoothAddress));
306 }
307 return null;
308 }
309
310
311
312
313
314
315 public boolean isCurrentThreadInterruptedCallback() {
316 return UtilsJavaSE.isCurrentThreadInterrupted();
317 }
318
319 public boolean authenticateRemoteDevice(long address) throws IOException {
320 return false;
321 }
322
323
324
325
326
327
328
329 public boolean authenticateRemoteDevice(long address, String passkey) throws IOException {
330 return false;
331 }
332
333
334
335 public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException {
336 initialized();
337 if (currentDeviceDiscoveryListener != null) {
338 throw new BluetoothStateException("Another inquiry already running");
339 }
340 currentDeviceDiscoveryListener = listener;
341 return DeviceInquiryThread.startInquiry(this, this, accessCode, listener);
342 }
343
344
345
346
347 private native boolean cancelInquiry();
348
349 public boolean cancelInquiry(DiscoveryListener listener) {
350 if (currentDeviceDiscoveryListener != listener) {
351 return false;
352 }
353
354 currentDeviceDiscoveryListener = null;
355 return cancelInquiry();
356 }
357
358
359
360
361 private native int runDeviceInquiryImpl(DeviceInquiryThread startedNotify, int accessCode, int duration,
362 DiscoveryListener listener) throws BluetoothStateException;
363
364 public int runDeviceInquiry(DeviceInquiryThread startedNotify, int accessCode, DiscoveryListener listener)
365 throws BluetoothStateException {
366 try {
367 deviceDiscoveryDevices = new Hashtable();
368 int discType = runDeviceInquiryImpl(startedNotify, accessCode, DeviceInquiryThread
369 .getConfigDeviceInquiryDuration(), listener);
370 if (discType == DiscoveryListener.INQUIRY_COMPLETED) {
371 for (Enumeration en = deviceDiscoveryDevices.keys(); en.hasMoreElements();) {
372 RemoteDevice remoteDevice = (RemoteDevice) en.nextElement();
373 DeviceClass deviceClass = (DeviceClass) deviceDiscoveryDevices.get(remoteDevice);
374 listener.deviceDiscovered(remoteDevice, deviceClass);
375
376 if (currentDeviceDiscoveryListener == null) {
377 return DiscoveryListener.INQUIRY_TERMINATED;
378 }
379 }
380 }
381 return discType;
382 } finally {
383 deviceDiscoveryDevices = null;
384 currentDeviceDiscoveryListener = null;
385 }
386 }
387
388
389
390
391
392
393 public void deviceDiscoveredCallback(DiscoveryListener listener, long deviceAddr, int deviceClass,
394 String deviceName, boolean paired) {
395 RemoteDevice remoteDevice = RemoteDeviceHelper.createRemoteDevice(this, deviceAddr, deviceName, paired);
396 if ((currentDeviceDiscoveryListener == null) || (deviceDiscoveryDevices == null)
397 || (currentDeviceDiscoveryListener != listener)) {
398 return;
399 }
400 DeviceClass cod = new DeviceClass(deviceClass);
401 DebugLog.debug("deviceDiscoveredCallback address", remoteDevice.getBluetoothAddress());
402 DebugLog.debug("deviceDiscoveredCallback deviceClass", cod);
403 deviceDiscoveryDevices.put(remoteDevice, cod);
404 }
405
406
407
408
409
410
411 public native int[] runSearchServices(UUID[] uuidSet, long address) throws SearchServicesException;
412
413
414
415
416 public native byte[] getServiceAttributes(int[] attrIDs, long address, int handle) throws IOException;
417
418 public int searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice device, DiscoveryListener listener)
419 throws BluetoothStateException {
420 return SearchServicesThread.startSearchServices(this, this, attrSet, uuidSet, device, listener);
421 }
422
423 public int runSearchServices(SearchServicesThread startedNotify, int[] attrSet, UUID[] uuidSet,
424 RemoteDevice device, DiscoveryListener listener) throws BluetoothStateException {
425 startedNotify.searchServicesStartedCallback();
426 int[] handles;
427 try {
428 handles = runSearchServices(uuidSet, RemoteDeviceHelper.getAddress(device));
429 } catch (SearchServicesDeviceNotReachableException e) {
430 return DiscoveryListener.SERVICE_SEARCH_DEVICE_NOT_REACHABLE;
431 } catch (SearchServicesTerminatedException e) {
432 return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
433 } catch (SearchServicesException e) {
434 return DiscoveryListener.SERVICE_SEARCH_ERROR;
435 }
436 if (handles == null) {
437 return DiscoveryListener.SERVICE_SEARCH_ERROR;
438 } else if (handles.length > 0) {
439 ServiceRecord[] records = new ServiceRecordImpl[handles.length];
440 int[] requiredAttrIDs = new int[] { BluetoothConsts.ServiceRecordHandle,
441 BluetoothConsts.ServiceClassIDList, BluetoothConsts.ServiceRecordState, BluetoothConsts.ServiceID,
442 BluetoothConsts.ProtocolDescriptorList };
443 boolean hasError = false;
444 for (int i = 0; i < handles.length; i++) {
445 records[i] = new ServiceRecordImpl(this, device, handles[i]);
446 try {
447 records[i].populateRecord(requiredAttrIDs);
448 if (attrSet != null) {
449 records[i].populateRecord(attrSet);
450 }
451 } catch (Exception e) {
452 DebugLog.debug("populateRecord error", e);
453 hasError = true;
454 }
455 if (startedNotify.isTerminated()) {
456 return DiscoveryListener.SERVICE_SEARCH_TERMINATED;
457 }
458 }
459 listener.servicesDiscovered(startedNotify.getTransID(), records);
460 if (hasError) {
461 return DiscoveryListener.SERVICE_SEARCH_ERROR;
462 } else {
463 return DiscoveryListener.SERVICE_SEARCH_COMPLETED;
464 }
465 } else {
466 return DiscoveryListener.SERVICE_SEARCH_NO_RECORDS;
467 }
468 }
469
470 public boolean cancelServiceSearch(int transID) {
471 SearchServicesThread sst = SearchServicesThread.getServiceSearchThread(transID);
472 if (sst != null) {
473 return sst.setTerminated();
474 } else {
475 return false;
476 }
477 }
478
479 public boolean populateServicesRecordAttributeValues(ServiceRecordImpl serviceRecord, int[] attrIDs)
480 throws IOException {
481 if (attrIDs.length > ATTR_RETRIEVABLE_MAX) {
482 throw new IllegalArgumentException();
483 }
484
485
486
487 byte[] blob = getServiceAttributes(attrIDs, RemoteDeviceHelper.getAddress(serviceRecord.getHostDevice()),
488 (int) serviceRecord.getHandle());
489
490 if (blob.length > 0) {
491 try {
492 boolean anyRetrived = false;
493 DataElement element = (new SDPInputStream(new ByteArrayInputStream(blob))).readElement();
494 for (Enumeration e = (Enumeration) element.getValue(); e.hasMoreElements();) {
495 int attrID = (int) ((DataElement) e.nextElement()).getLong();
496 serviceRecord.populateAttributeValue(attrID, (DataElement) e.nextElement());
497 if (!anyRetrived) {
498 for (int i = 0; i < attrIDs.length; i++) {
499 if (attrIDs[i] == attrID) {
500 anyRetrived = true;
501 break;
502 }
503 }
504 }
505 }
506 return anyRetrived;
507 } catch (IOException e) {
508 throw e;
509 } catch (Throwable e) {
510 throw new IOException();
511 }
512 } else {
513 return false;
514 }
515 }
516
517
518
519
520 private native long socket(boolean authenticate, boolean encrypt) throws IOException;
521
522 private native long getsockaddress(long socket) throws IOException;
523
524 private native void storesockopt(long socket);
525
526 private native int getsockchannel(long socket) throws IOException;
527
528 private native void connect(long socket, long address, int channel) throws IOException;
529
530 private native void bind(long socket) throws IOException;
531
532 private native void listen(long socket) throws IOException;
533
534 private native long accept(long socket) throws IOException;
535
536 private native int recvAvailable(long socket) throws IOException;
537
538 private native int recv(long socket) throws IOException;
539
540 private native int recv(long socket, byte[] b, int off, int len) throws IOException;
541
542 private native void send(long socket, int b) throws IOException;
543
544 private native void send(long socket, byte[] b, int off, int len) throws IOException;
545
546 private native void close(long socket) throws IOException;
547
548 private native String getpeername(long address) throws IOException;
549
550 private native long getpeeraddress(long socket) throws IOException;
551
552
553
554
555
556
557
558
559
560 public long connectionRfOpenClientConnection(BluetoothConnectionParams params) throws IOException {
561 long socket = socket(params.authenticate, params.encrypt);
562 boolean success = false;
563 try {
564 connect(socket, params.address, params.channel);
565 success = true;
566 } finally {
567 if (!success) {
568 close(socket);
569 }
570 }
571 return socket;
572 }
573
574 public void connectionRfCloseClientConnection(long handle) throws IOException {
575 close(handle);
576 }
577
578 public long rfServerOpen(BluetoothConnectionNotifierParams params, ServiceRecordImpl serviceRecord)
579 throws IOException {
580
581
582
583 long socket = socket(params.authenticate, params.encrypt);
584 boolean success = false;
585 try {
586
587 synchronized (this) {
588 bind(socket);
589 }
590 listen(socket);
591
592 int channel = getsockchannel(socket);
593 DebugLog.debug("service channel ", channel);
594
595 long serviceRecordHandle = socket;
596 serviceRecord.populateRFCOMMAttributes(serviceRecordHandle, channel, params.uuid, params.name, params.obex);
597
598
599
600
601 serviceRecord.setHandle(registerService(serviceRecord.toByteArray(), serviceRecord.deviceServiceClasses));
602
603 success = true;
604 } finally {
605 if (!success) {
606 try {
607 close(socket);
608 } catch (IOException e) {
609 DebugLog.debug("close on failure", e);
610 }
611 }
612 }
613 return socket;
614 }
615
616 public void rfServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
617 try {
618
619
620
621 close(handle);
622 } finally {
623
624
625
626 unregisterService(serviceRecord.getHandle());
627 }
628 }
629
630
631
632
633 private native long registerService(byte[] record, int classOfDevice) throws ServiceRegistrationException;
634
635
636
637
638 private native void unregisterService(long handle) throws ServiceRegistrationException;
639
640 public long rfServerAcceptAndOpenRfServerConnection(long handle) throws IOException {
641 return accept(handle);
642 }
643
644 public void rfServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
645 throws ServiceRegistrationException {
646 unregisterService(serviceRecord.getHandle());
647 byte[] blob;
648 try {
649 blob = serviceRecord.toByteArray();
650 } catch (IOException e) {
651 throw new ServiceRegistrationException(e.toString());
652 }
653 serviceRecord.setHandle(registerService(blob, serviceRecord.deviceServiceClasses));
654 DebugLog.debug("new serviceRecord", serviceRecord);
655 }
656
657 public void connectionRfCloseServerConnection(long handle) throws IOException {
658 connectionRfCloseClientConnection(handle);
659 }
660
661 public long getConnectionRfRemoteAddress(long handle) throws IOException {
662 return getpeeraddress(handle);
663 }
664
665 public int connectionRfRead(long handle) throws IOException {
666 return recv(handle);
667 }
668
669 public int connectionRfRead(long handle, byte[] b, int off, int len) throws IOException {
670 return recv(handle, b, off, len);
671 }
672
673 public int connectionRfReadAvailable(long handle) throws IOException {
674 return recvAvailable(handle);
675 }
676
677 public void connectionRfWrite(long handle, int b) throws IOException {
678 send(handle, b);
679 }
680
681 public void connectionRfWrite(long handle, byte[] b, int off, int len) throws IOException {
682 send(handle, b, off, len);
683 }
684
685 public void connectionRfFlush(long handle) throws IOException {
686
687 }
688
689 public int rfGetSecurityOpt(long handle, int expected) throws IOException {
690 return expected;
691 }
692
693
694
695
696
697
698 public boolean rfEncrypt(long address, long handle, boolean on) throws IOException {
699 return false;
700 }
701
702
703
704
705
706
707
708
709
710 public long l2OpenClientConnection(BluetoothConnectionParams params, int receiveMTU, int transmitMTU)
711 throws IOException {
712 throw new NotSupportedIOException(getStackID());
713 }
714
715
716
717
718
719
720 public void l2CloseClientConnection(long handle) throws IOException {
721 throw new NotSupportedIOException(getStackID());
722 }
723
724
725
726
727
728
729
730 public long l2ServerOpen(BluetoothConnectionNotifierParams params, int receiveMTU, int transmitMTU,
731 ServiceRecordImpl serviceRecord) throws IOException {
732 throw new NotSupportedIOException(getStackID());
733 }
734
735
736
737
738
739
740
741 public void l2ServerUpdateServiceRecord(long handle, ServiceRecordImpl serviceRecord, boolean acceptAndOpen)
742 throws ServiceRegistrationException {
743 throw new ServiceRegistrationException("Not Supported on" + getStackID());
744 }
745
746
747
748
749
750
751 public long l2ServerAcceptAndOpenServerConnection(long handle) throws IOException {
752 throw new NotSupportedIOException(getStackID());
753 }
754
755
756
757
758
759
760 public void l2CloseServerConnection(long handle) throws IOException {
761 throw new NotSupportedIOException(getStackID());
762 }
763
764
765
766
767
768
769
770 public void l2ServerClose(long handle, ServiceRecordImpl serviceRecord) throws IOException {
771 throw new NotSupportedIOException(getStackID());
772 }
773
774
775
776
777
778
779 public int l2GetSecurityOpt(long handle, int expected) throws IOException {
780 throw new NotSupportedIOException(getStackID());
781 }
782
783
784
785
786
787
788 public boolean l2Ready(long handle) throws IOException {
789 throw new NotSupportedIOException(getStackID());
790 }
791
792
793
794
795
796
797 public int l2Receive(long handle, byte[] inBuf) throws IOException {
798 throw new NotSupportedIOException(getStackID());
799 }
800
801
802
803
804
805
806 public void l2Send(long handle, byte[] data) throws IOException {
807 throw new NotSupportedIOException(getStackID());
808 }
809
810
811
812
813
814
815 public int l2GetReceiveMTU(long handle) throws IOException {
816 throw new NotSupportedIOException(getStackID());
817 }
818
819
820
821
822
823
824 public int l2GetTransmitMTU(long handle) throws IOException {
825 throw new NotSupportedIOException(getStackID());
826 }
827
828
829
830
831
832
833 public long l2RemoteAddress(long handle) throws IOException {
834 throw new NotSupportedIOException(getStackID());
835 }
836
837
838
839
840
841
842 public boolean l2Encrypt(long address, long handle, boolean on) throws IOException {
843 throw new NotSupportedIOException(getStackID());
844 }
845 }