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