1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package com.intel.bluetooth;
26
27 import java.io.IOException;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Vector;
31
32 import javax.bluetooth.BluetoothStateException;
33 import javax.bluetooth.DiscoveryAgent;
34 import javax.bluetooth.RemoteDevice;
35 import javax.bluetooth.ServiceRecord;
36 import javax.microedition.io.Connection;
37
38 import com.intel.bluetooth.WeakVectorFactory.WeakVector;
39
40
41
42
43
44
45
46
47
48
49
50
51 public abstract class RemoteDeviceHelper {
52
53 private static class RemoteDeviceWithExtendedInfo extends RemoteDevice {
54
55 String name;
56
57 long addressLong;
58
59 BluetoothStack bluetoothStack;
60
61 private Hashtable stackAttributes;
62
63 private boolean paired;
64
65
66
67
68 private WeakVector connections;
69
70 private RemoteDeviceWithExtendedInfo(BluetoothStack bluetoothStack, long address, String name) {
71 super(RemoteDeviceHelper.getBluetoothAddress(address));
72 this.bluetoothStack = bluetoothStack;
73 this.name = name;
74 this.addressLong = address;
75 }
76
77 private void addConnection(Object connection) {
78 synchronized (this) {
79 if (connections == null) {
80 connections = WeakVectorFactory.createWeakVector();
81 }
82 }
83 synchronized (connections) {
84 connections.addElement(connection);
85 DebugLog.debug("connection open, open now", connections.size());
86 }
87 }
88
89 private void removeConnection(Object connection) {
90 if (connections == null) {
91 return;
92 }
93 synchronized (connections) {
94 connections.removeElement(connection);
95 DebugLog.debug("connection closed, open now", connections.size());
96 }
97 }
98
99 void shutdownConnections() {
100 if (!hasConnections()) {
101 return;
102 }
103 Vector c2shutdown = new Vector();
104 synchronized (connections) {
105 c2shutdown = Utils.clone(connections.elements());
106 }
107 for (Enumeration en = c2shutdown.elements(); en.hasMoreElements();) {
108 BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
109 try {
110 c.shutdown();
111 } catch (IOException e) {
112 DebugLog.debug("connection shutdown", e);
113 }
114 }
115 synchronized (connections) {
116 connections.removeAllElements();
117 }
118 }
119
120 private void setStackAttributes(Object key, Object value) {
121 if (stackAttributes == null) {
122 stackAttributes = new Hashtable();
123 }
124 if (value == null) {
125 stackAttributes.remove(key);
126 } else {
127 stackAttributes.put(key, value);
128 }
129 }
130
131 private Object getStackAttributes(Object key) {
132 if (stackAttributes == null) {
133 return null;
134 }
135 return stackAttributes.get(key);
136 }
137
138 public String toString() {
139 return super.getBluetoothAddress();
140 }
141
142 int connectionsCount() {
143 if (connections == null) {
144 return 0;
145 }
146 return connections.size();
147 }
148
149 boolean hasConnections() {
150 return (connectionsCount() != 0);
151 }
152
153
154
155
156 public boolean authenticate() throws IOException {
157 if (!hasConnections()) {
158 throw new IOException("No open connections to this RemoteDevice");
159 }
160 if (this.isAuthenticated()) {
161
162 return true;
163 }
164 boolean authenticated = bluetoothStack.authenticateRemoteDevice(addressLong);
165 paired = authenticated;
166 if (authenticated) {
167 updateConnectionMarkAuthenticated();
168 }
169 return authenticated;
170 }
171
172
173
174
175 boolean authenticate(String passkey) throws IOException {
176 boolean authenticated = bluetoothStack.authenticateRemoteDevice(addressLong, passkey);
177 paired = authenticated;
178 if (authenticated) {
179 updateConnectionMarkAuthenticated();
180 }
181 return authenticated;
182 }
183
184 void removeAuthentication() throws IOException {
185 bluetoothStack.removeAuthenticationWithRemoteDevice(addressLong);
186 paired = false;
187 }
188
189 private void updateConnectionMarkAuthenticated() {
190 if (connections == null) {
191 return;
192 }
193 synchronized (connections) {
194 for (Enumeration en = connections.elements(); en.hasMoreElements();) {
195 BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
196 c.markAuthenticated();
197 }
198 }
199 }
200
201
202
203
204
205
206
207 public boolean authorize(Connection conn) throws IOException {
208 if (!(conn instanceof BluetoothConnectionAccess)) {
209 throw new IllegalArgumentException("Connection is not a Bluetooth connection");
210 }
211 if (((BluetoothConnectionAccess) conn).isClosed()) {
212 throw new IOException("Connection is already closed");
213 }
214 if (!(conn instanceof BluetoothServerConnection)) {
215 throw new IllegalArgumentException("Connection is not an incomming Bluetooth connection");
216 }
217 return isTrustedDevice() || isAuthenticated();
218 }
219
220
221
222
223
224 public boolean isAuthorized(Connection conn) throws IOException {
225 if (!(conn instanceof BluetoothConnectionAccess)) {
226 throw new IllegalArgumentException("Connection is not a Bluetooth connection");
227 }
228 if (((BluetoothConnectionAccess) conn).isClosed()) {
229 throw new IOException("Connection is already closed");
230 }
231 if (!(conn instanceof BluetoothServerConnection)) {
232 throw new IllegalArgumentException("Connection is not an incomming Bluetooth connection");
233 }
234 return isTrustedDevice();
235 }
236
237
238
239
240
241
242 public boolean encrypt(Connection conn, boolean on) throws IOException {
243 if (!(conn instanceof BluetoothConnectionAccess)) {
244 throw new IllegalArgumentException("Connection is not a Bluetooth connection");
245 }
246 if (((BluetoothConnectionAccess) conn).getRemoteAddress() != this.addressLong) {
247 throw new IllegalArgumentException("Connection is not to this device");
248 }
249 if ((((BluetoothConnectionAccess) conn).getSecurityOpt() == ServiceRecord.AUTHENTICATE_ENCRYPT) == on) {
250 return true;
251 }
252 return ((BluetoothConnectionAccess) conn).encrypt(this.addressLong, on);
253 }
254
255
256
257
258
259
260 public boolean isAuthenticated() {
261 if (!hasConnections()) {
262 DebugLog.debug("no connections, Authenticated = false");
263 return false;
264 }
265 Boolean authenticated = bluetoothStack.isRemoteDeviceAuthenticated(addressLong);
266 if (authenticated != null) {
267 return authenticated.booleanValue();
268 }
269 synchronized (connections) {
270
271 for (Enumeration en = connections.elements(); en.hasMoreElements();) {
272 BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
273 if (c.getSecurityOpt() != ServiceRecord.NOAUTHENTICATE_NOENCRYPT) {
274 return true;
275 }
276 }
277 }
278 return false;
279 }
280
281
282
283
284
285
286 public boolean isEncrypted() {
287 if (!hasConnections()) {
288 return false;
289 }
290 synchronized (connections) {
291
292 for (Enumeration en = connections.elements(); en.hasMoreElements();) {
293 BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
294 if (c.getSecurityOpt() == ServiceRecord.AUTHENTICATE_ENCRYPT) {
295 return true;
296 }
297 }
298 }
299 return false;
300 }
301
302
303
304
305
306
307 public boolean isTrustedDevice() {
308 Boolean trusted = bluetoothStack.isRemoteDeviceTrusted(addressLong);
309 if (trusted == null) {
310 return paired;
311 } else {
312 return trusted.booleanValue();
313 }
314 }
315 }
316
317 private static Hashtable stackDevicesCashed = new Hashtable();
318
319 private RemoteDeviceHelper() {
320
321 }
322
323 private static synchronized Hashtable devicesCashed(BluetoothStack bluetoothStack) {
324 Hashtable devicesCashed = (Hashtable) stackDevicesCashed.get(bluetoothStack);
325 if (devicesCashed == null) {
326 devicesCashed = new Hashtable();
327 stackDevicesCashed.put(bluetoothStack, devicesCashed);
328 }
329 return devicesCashed;
330 }
331
332 private static RemoteDeviceWithExtendedInfo getCashedDeviceWithExtendedInfo(BluetoothStack bluetoothStack,
333 long address) {
334 Object key = new Long(address);
335 return (RemoteDeviceWithExtendedInfo) devicesCashed(bluetoothStack).get(key);
336 }
337
338 static RemoteDevice getCashedDevice(BluetoothStack bluetoothStack, long address) {
339 return getCashedDeviceWithExtendedInfo(bluetoothStack, address);
340 }
341
342 static RemoteDevice createRemoteDevice(BluetoothStack bluetoothStack, long address, String name, boolean paired) {
343 RemoteDeviceWithExtendedInfo dev = getCashedDeviceWithExtendedInfo(bluetoothStack, address);
344 if (dev == null) {
345 Object saveID = BlueCoveImpl.getCurrentThreadBluetoothStackID();
346 try {
347 BlueCoveImpl.setThreadBluetoothStack(bluetoothStack);
348 dev = new RemoteDeviceWithExtendedInfo(bluetoothStack, address, name);
349 } finally {
350 if (saveID != null) {
351 BlueCoveImpl.setThreadBluetoothStackID(saveID);
352 }
353 }
354 devicesCashed(bluetoothStack).put(new Long(address), dev);
355 DebugLog.debug0x("new devicesCashed", address);
356 } else if (!Utils.isStringSet(dev.name)) {
357
358 dev.name = name;
359 } else if (Utils.isStringSet(name)) {
360
361 dev.name = name;
362 }
363 if (paired) {
364 dev.paired = paired;
365 }
366 return dev;
367 }
368
369 private static BluetoothStack getBluetoothStack() throws RuntimeException {
370 try {
371 return BlueCoveImpl.instance().getBluetoothStack();
372 } catch (BluetoothStateException e) {
373 throw (RuntimeException) UtilsJavaSE.initCause(new RuntimeException("Can't initialize bluetooth support"),
374 e);
375 }
376 }
377
378 private static RemoteDeviceWithExtendedInfo remoteDeviceImpl(RemoteDevice device) {
379 return (RemoteDeviceWithExtendedInfo) createRemoteDevice(null, device);
380 }
381
382 static RemoteDevice createRemoteDevice(BluetoothStack bluetoothStack, RemoteDevice device) throws RuntimeException {
383 if (device instanceof RemoteDeviceWithExtendedInfo) {
384 return device;
385 } else {
386 if (bluetoothStack == null) {
387 bluetoothStack = getBluetoothStack();
388 }
389 return createRemoteDevice(bluetoothStack, getAddress(device), null, false);
390 }
391 }
392
393 public static String getFriendlyName(RemoteDevice device, long address, boolean alwaysAsk) throws IOException {
394 String name = null;
395 if (!(device instanceof RemoteDeviceWithExtendedInfo)) {
396 device = createRemoteDevice(null, device);
397 }
398 name = ((RemoteDeviceWithExtendedInfo) device).name;
399 if (alwaysAsk || (!Utils.isStringSet(name))) {
400 name = ((RemoteDeviceWithExtendedInfo) device).bluetoothStack.getRemoteDeviceFriendlyName(address);
401 if (Utils.isStringSet(name)) {
402 ((RemoteDeviceWithExtendedInfo) device).name = name;
403 } else {
404 throw new IOException("Can't query remote device");
405 }
406 }
407 return name;
408 }
409
410
411
412
413 public static RemoteDevice getRemoteDevice(Connection conn) throws IOException {
414 if (!(conn instanceof BluetoothConnectionAccess)) {
415 throw new IllegalArgumentException("Not a Bluetooth connection " + conn.getClass().getName());
416 }
417 return createRemoteDevice(((BluetoothConnectionAccess) conn).getBluetoothStack(),
418 ((BluetoothConnectionAccess) conn).getRemoteAddress(), null, false);
419 }
420
421
422
423
424
425
426 public static RemoteDevice[] retrieveDevices(BluetoothStack bluetoothStack, int option) {
427 if ((option != DiscoveryAgent.PREKNOWN) && (option != DiscoveryAgent.CACHED)) {
428 throw new IllegalArgumentException("invalid option");
429 }
430 RemoteDevice[] impl = bluetoothStack.retrieveDevices(option);
431 if (impl != null) {
432 if (impl.length == 0) {
433
434 return null;
435 } else {
436 return impl;
437 }
438 }
439
440 Hashtable devicesCashed = devicesCashed(bluetoothStack);
441 switch (option) {
442 case DiscoveryAgent.PREKNOWN:
443 if (devicesCashed.size() == 0) {
444
445 return null;
446 }
447 Vector devicesPaired = new Vector();
448 for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
449 RemoteDeviceWithExtendedInfo d = (RemoteDeviceWithExtendedInfo) en.nextElement();
450 if (d.isTrustedDevice()) {
451 devicesPaired.addElement(d);
452 }
453 }
454 if (devicesPaired.size() == 0) {
455
456 return null;
457 }
458 return remoteDeviceListToArray(devicesPaired);
459 case DiscoveryAgent.CACHED:
460 if (devicesCashed.size() == 0) {
461
462 return null;
463 }
464 RemoteDevice[] devices = new RemoteDevice[devicesCashed.size()];
465 int k = 0;
466 for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
467 devices[k++] = (RemoteDevice) en.nextElement();
468 }
469 return devices;
470 default:
471 throw new IllegalArgumentException("invalid option");
472 }
473 }
474
475 static RemoteDevice[] remoteDeviceListToArray(Vector devices) {
476 RemoteDevice[] devicesArray = new RemoteDevice[devices.size()];
477 int i = 0;
478 for (Enumeration en = devices.elements(); en.hasMoreElements();) {
479 devicesArray[i++] = (RemoteDevice) en.nextElement();
480 }
481 return devicesArray;
482 }
483
484
485
486
487
488
489 public static int openConnections() {
490 int c = 0;
491 Hashtable devicesCashed = devicesCashed(getBluetoothStack());
492 synchronized (devicesCashed) {
493 for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
494 c += ((RemoteDeviceWithExtendedInfo) en.nextElement()).connectionsCount();
495 }
496 }
497 return c;
498 }
499
500
501
502
503
504
505 public static int openConnections(long address) {
506 RemoteDeviceWithExtendedInfo dev = getCashedDeviceWithExtendedInfo(getBluetoothStack(), address);
507 if (dev == null) {
508 return 0;
509 }
510 return dev.connectionsCount();
511 }
512
513
514
515
516
517
518 public static int connectedDevices() {
519 int c = 0;
520 Hashtable devicesCashed = devicesCashed(getBluetoothStack());
521 synchronized (devicesCashed) {
522 for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
523 if (((RemoteDeviceWithExtendedInfo) en.nextElement()).hasConnections()) {
524 c++;
525 }
526 }
527 }
528 return c;
529 }
530
531 static void shutdownConnections(BluetoothStack bluetoothStack) {
532 Hashtable devicesCashed = devicesCashed(bluetoothStack);
533 synchronized (devicesCashed) {
534 for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
535 ((RemoteDeviceWithExtendedInfo) en.nextElement()).shutdownConnections();
536 }
537 }
538 }
539
540 public static String formatBluetoothAddress(String address) {
541 String s = address.toUpperCase();
542 return "000000000000".substring(s.length()) + s;
543 }
544
545 public static String getBluetoothAddress(long address) {
546 return formatBluetoothAddress(Utils.toHexString(address));
547 }
548
549 public static long getAddress(String bluetoothAddress) {
550 if (bluetoothAddress.indexOf('-') != -1) {
551 throw new IllegalArgumentException("Illegal bluetoothAddress {" + bluetoothAddress + "}");
552 }
553 try {
554 return Long.parseLong(bluetoothAddress, 16);
555 } catch (NumberFormatException e) {
556 throw new IllegalArgumentException("Illegal bluetoothAddress {" + bluetoothAddress + "}; should be hex number");
557 }
558 }
559
560 static long getAddress(RemoteDevice device) {
561 if (device instanceof RemoteDeviceWithExtendedInfo) {
562 return ((RemoteDeviceWithExtendedInfo) device).addressLong;
563 } else {
564 return getAddress(device.getBluetoothAddress());
565 }
566 }
567
568 static void setStackAttributes(BluetoothStack bluetoothStack, RemoteDevice device, Object key, Object value) {
569 RemoteDeviceWithExtendedInfo devInfo = (RemoteDeviceWithExtendedInfo) createRemoteDevice(bluetoothStack, device);
570 devInfo.setStackAttributes(key, value);
571 }
572
573 static Object getStackAttributes(BluetoothStack bluetoothStack, RemoteDevice device, Object key) {
574 RemoteDeviceWithExtendedInfo devInfo = null;
575 if (device instanceof RemoteDeviceWithExtendedInfo) {
576 devInfo = (RemoteDeviceWithExtendedInfo) device;
577 } else {
578 devInfo = getCashedDeviceWithExtendedInfo(bluetoothStack, getAddress(device));
579 }
580
581 if (devInfo != null) {
582 return devInfo.getStackAttributes(key);
583 } else {
584 return null;
585 }
586 }
587
588 static void connected(BluetoothConnectionAccess connection) throws IOException {
589 RemoteDeviceWithExtendedInfo device = (RemoteDeviceWithExtendedInfo) getRemoteDevice((Connection) connection);
590 connection.setRemoteDevice(device);
591 device.addConnection(connection);
592 }
593
594 static void disconnected(BluetoothConnectionAccess connection) {
595 RemoteDevice d = connection.getRemoteDevice();
596 if (d != null) {
597 ((RemoteDeviceWithExtendedInfo) d).removeConnection(connection);
598 connection.setRemoteDevice(null);
599 }
600 }
601
602
603
604
605
606
607 public static boolean authenticate(RemoteDevice device) throws IOException {
608 return remoteDeviceImpl(device).authenticate();
609 }
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626 public static boolean authenticate(RemoteDevice device, String passkey) throws IOException {
627 return remoteDeviceImpl(device).authenticate(passkey);
628 }
629
630
631
632
633
634
635
636
637
638
639
640
641
642 public static void removeAuthentication(RemoteDevice device) throws IOException {
643 remoteDeviceImpl(device).removeAuthentication();
644 }
645
646
647
648
649
650
651
652 public static boolean authorize(RemoteDevice device, Connection conn) throws IOException {
653 return remoteDeviceImpl(device).authorize(conn);
654 }
655
656
657
658
659
660
661 public static boolean encrypt(RemoteDevice device, Connection conn, boolean on) throws IOException {
662 return remoteDeviceImpl(device).encrypt(conn, on);
663 }
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678 public static boolean isAuthenticated(RemoteDevice device) {
679 return remoteDeviceImpl(device).isAuthenticated();
680 }
681
682 public static boolean isAuthorized(RemoteDevice device, Connection conn) throws IOException {
683 return remoteDeviceImpl(device).isAuthorized(conn);
684 }
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699 public static boolean isEncrypted(RemoteDevice device) {
700 return remoteDeviceImpl(device).isEncrypted();
701 }
702
703 public static boolean isTrustedDevice(RemoteDevice device) {
704 return remoteDeviceImpl(device).isTrustedDevice();
705 }
706 }