View Javadoc

1   /**
2    *  BlueCove - Java library for Bluetooth
3    *  Copyright (C) 2006-2008 Vlad Skarzhevskyy
4    *
5    *  This library is free software; you can redistribute it and/or
6    *  modify it under the terms of the GNU Lesser General Public
7    *  License as published by the Free Software Foundation; either
8    *  version 2.1 of the License, or (at your option) any later version.
9    *
10   *  This library is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   *  Lesser General Public License for more details.
14   *
15   *  You should have received a copy of the GNU Lesser General Public
16   *  License along with this library; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   *
19   *  @version $Id: RemoteDeviceHelper.java 2366 2008-07-21 15:51:14Z skarzhevskyy $
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   * Implementation of RemoteDevice.
38   * 
39   * Instance of RemoteDevice can be created by User. BlueCove should use only
40   * RemoteDeviceHelper class to create RemoteDevice instances.
41   * 
42   * <p>
43   * <b><u>Your application should not use this class directly.</u></b>
44   * 
45   * The only exception is method authenticate(RemoteDevice device, String
46   * passkey).
47   * 
48   * @author vlads
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  		 * Connections can be discarded by the garbage collector.
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 		 * @see javax.bluetooth.RemoteDevice#authenticate()
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 				// has previously been authenticated
161 				return true;
162 			}
163 			boolean authenticated = bluetoothStack.authenticateRemoteDevice(addressLong);
164 			if (authenticated) {
165 				updateConnectionMarkAuthenticated();
166 			}
167 			return authenticated;
168 		}
169 
170 		/**
171 		 * @see com.intel.bluetooth.RemoteDeviceHelper#authenticateRemoteDevice(RemoteDevice,
172 		 *      java.lang.String)
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 		 * Determines if this RemoteDevice should be allowed to continue to
196 		 * access the local service provided by the Connection.
197 		 * 
198 		 * @see javax.bluetooth.RemoteDevice#authorize(javax.microedition.io.Connection)
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 		 * @see javax.bluetooth.RemoteDevice#isAuthorized(javax.microedition.io.Connection)
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 		 * Attempts to turn encryption on or off for an existing connection.
232 		 * 
233 		 * @see javax.bluetooth.RemoteDevice#encrypt(javax.microedition.io.Connection,
234 		 *      boolean)
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 		 * (non-Javadoc)
251 		 * 
252 		 * @see javax.bluetooth.RemoteDevice#isAuthenticated()
253 		 */
254 		public boolean isAuthenticated() {
255 			if (!hasConnections()) {
256 				DebugLog.debug("no connections, Authenticated = false");
257 				return false;
258 			}
259 			synchronized (connections) {
260 				// Find first authenticated connection
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 		 * (non-Javadoc)
273 		 * 
274 		 * @see javax.bluetooth.RemoteDevice#isEncrypted()
275 		 */
276 		public boolean isEncrypted() {
277 			if (!hasConnections()) {
278 				return false;
279 			}
280 			synchronized (connections) {
281 				// Find first encrypted connection
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 		 * (non-Javadoc)
294 		 * 
295 		 * @see javax.bluetooth.RemoteDevice#isTrustedDevice()
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 			// New name found
343 			dev.name = name;
344 		} else if (Utils.isStringSet(name)) {
345 			// Update name if changed
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 	 * @see javax.bluetooth.RemoteDevice#getRemoteDevice(Connection)
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 	 * (non-Javadoc)
408 	 * 
409 	 * @see javax.bluetooth.DiscoveryAgent#retrieveDevices(int)
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 				// Spec: null if no devices meet the criteria
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 				// Spec: null if no devices meet the criteria
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 				// Spec: null if no devices meet the criteria
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 	 * Count total number of open connections to all devices.
454 	 * 
455 	 * @return number of connections
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 	 * Count number of open connections to or from specific device.
470 	 * 
471 	 * @return number of connections
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 	 * Count number of device that have open connections to or from them.
483 	 * 
484 	 * @return number of connections
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 	 * Attempts to authenticate RemoteDevice. Return <code>false</code> if the
572 	 * stack does not support authentication.
573 	 * 
574 	 * @see javax.bluetooth.RemoteDevice#authenticate()
575 	 */
576 	public static boolean authenticate(RemoteDevice device) throws IOException {
577 		return remoteDeviceImpl(device).authenticate();
578 	}
579 
580 	/**
581 	 * 
582 	 * Sends an authentication request to a remote Bluetooth device. Non JSR-82,
583 	 * Return <code>false</code> if the stack does not support authentication.
584 	 * <p>
585 	 * <b>PUBLIC JSR-82 extension</b>
586 	 * 
587 	 * @param device
588 	 *            Remote Device
589 	 * @param passkey
590 	 *            A Personal Identification Number (PIN) to be used for device
591 	 *            authentication.
592 	 * @return <code>true</code> if authentication is successful; otherwise
593 	 *         <code>false</code>
594 	 * @throws IOException
595 	 *             if there are error during authentication.
596 	 */
597 	public static boolean authenticate(RemoteDevice device, String passkey) throws IOException {
598 		return remoteDeviceImpl(device).authenticate(passkey);
599 	}
600 
601 	/**
602 	 * Determines if this RemoteDevice should be allowed to continue to access
603 	 * the local service provided by the Connection.
604 	 * 
605 	 * @see javax.bluetooth.RemoteDevice#authorize(javax.microedition.io.Connection)
606 	 */
607 	public static boolean authorize(RemoteDevice device, Connection conn) throws IOException {
608 		return remoteDeviceImpl(device).authorize(conn);
609 	}
610 
611 	/**
612 	 * Attempts to turn encryption on or off for an existing connection.
613 	 * 
614 	 * @see javax.bluetooth.RemoteDevice#encrypt(javax.microedition.io.Connection,
615 	 *      boolean)
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 	 * Determines if this <code>RemoteDevice</code> has been authenticated.
623 	 * <P>
624 	 * A device may have been authenticated by this application or another
625 	 * application. Authentication applies to an ACL link between devices and
626 	 * not on a specific L2CAP, RFCOMM, or OBEX connection. Therefore, if
627 	 * <code>authenticate()</code> is performed when an L2CAP connection is
628 	 * made to device A, then <code>isAuthenticated()</code> may return
629 	 * <code>true</code> when tested as part of making an RFCOMM connection to
630 	 * device A.
631 	 * 
632 	 * @return <code>true</code> if this <code>RemoteDevice</code> has
633 	 *         previously been authenticated; <code>false</code> if it has not
634 	 *         been authenticated or there are no open connections between the
635 	 *         local device and this <code>RemoteDevice</code>
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 	 * Determines if data exchanges with this <code>RemoteDevice</code> are
647 	 * currently being encrypted.
648 	 * <P>
649 	 * Encryption may have been previously turned on by this or another
650 	 * application. Encryption applies to an ACL link between devices and not on
651 	 * a specific L2CAP, RFCOMM, or OBEX connection. Therefore, if
652 	 * <code>encrypt()</code> is performed with the <code>on</code>
653 	 * parameter set to <code>true</code> when an L2CAP connection is made to
654 	 * device A, then <code>isEncrypted()</code> may return <code>true</code>
655 	 * when tested as part of making an RFCOMM connection to device A.
656 	 * 
657 	 * @return <code>true</code> if data exchanges with this
658 	 *         <code>RemoteDevice</code> are being encrypted;
659 	 *         <code>false</code> if they are not being encrypted, or there
660 	 *         are no open connections between the local device and this
661 	 *         <code>RemoteDevice</code>
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 }