com.intel.bluetooth.RemoteDeviceHelper Maven / Gradle / Ivy
/**
* BlueCove - Java library for Bluetooth
* Copyright (C) 2006-2008 Vlad Skarzhevskyy
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @version $Id: RemoteDeviceHelper.java 2366 2008-07-21 15:51:14Z skarzhevskyy $
*/
package com.intel.bluetooth;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.microedition.io.Connection;
import com.intel.bluetooth.WeakVectorFactory.WeakVector;
/**
* Implementation of RemoteDevice.
*
* Instance of RemoteDevice can be created by User. BlueCove should use only
* RemoteDeviceHelper class to create RemoteDevice instances.
*
*
* Your application should not use this class directly.
*
* The only exception is method authenticate(RemoteDevice device, String
* passkey).
*
* @author vlads
*/
public abstract class RemoteDeviceHelper {
private static class RemoteDeviceWithExtendedInfo extends RemoteDevice {
String name;
long addressLong;
BluetoothStack bluetoothStack;
private Hashtable stackAttributes;
private boolean paired;
/**
* Connections can be discarded by the garbage collector.
*/
private WeakVector connections;
private RemoteDeviceWithExtendedInfo(BluetoothStack bluetoothStack, long address, String name) {
super(RemoteDeviceHelper.getBluetoothAddress(address));
this.bluetoothStack = bluetoothStack;
this.name = name;
this.addressLong = address;
}
private void addConnection(Object connection) {
synchronized (this) {
if (connections == null) {
connections = WeakVectorFactory.createWeakVector();
}
}
synchronized (connections) {
connections.addElement(connection);
DebugLog.debug("connection open, open now", connections.size());
}
}
private void removeConnection(Object connection) {
if (connections == null) {
return;
}
synchronized (connections) {
connections.removeElement(connection);
DebugLog.debug("connection closed, open now", connections.size());
}
}
void shutdownConnections() {
if (!hasConnections()) {
return;
}
Vector c2shutdown = new Vector();
synchronized (connections) {
c2shutdown = Utils.clone(connections.elements());
}
for (Enumeration en = c2shutdown.elements(); en.hasMoreElements();) {
BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
try {
c.shutdown();
} catch (IOException e) {
DebugLog.debug("connection shutdown", e);
}
}
synchronized (connections) {
connections.removeAllElements();
}
}
private void setStackAttributes(Object key, Object value) {
if (stackAttributes == null) {
stackAttributes = new Hashtable();
}
if (value == null) {
stackAttributes.remove(key);
} else {
stackAttributes.put(key, value);
}
}
private Object getStackAttributes(Object key) {
if (stackAttributes == null) {
return null;
}
return stackAttributes.get(key);
}
public String toString() {
return super.getBluetoothAddress();
}
int connectionsCount() {
if (connections == null) {
return 0;
}
return connections.size();
}
boolean hasConnections() {
return (connectionsCount() != 0);
}
/**
* @see javax.bluetooth.RemoteDevice#authenticate()
*/
public boolean authenticate() throws IOException {
if (!hasConnections()) {
throw new IOException("No open connections to this RemoteDevice");
}
if (this.isAuthenticated()) {
// has previously been authenticated
return true;
}
boolean authenticated = bluetoothStack.authenticateRemoteDevice(addressLong);
if (authenticated) {
updateConnectionMarkAuthenticated();
}
return authenticated;
}
/**
* @see com.intel.bluetooth.RemoteDeviceHelper#authenticateRemoteDevice(RemoteDevice,
* java.lang.String)
*/
public boolean authenticate(String passkey) throws IOException {
boolean authenticated = bluetoothStack.authenticateRemoteDevice(addressLong, passkey);
if (authenticated) {
updateConnectionMarkAuthenticated();
}
return authenticated;
}
private void updateConnectionMarkAuthenticated() {
if (connections == null) {
return;
}
synchronized (connections) {
for (Enumeration en = connections.elements(); en.hasMoreElements();) {
BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
c.markAuthenticated();
}
}
}
/**
* Determines if this RemoteDevice should be allowed to continue to
* access the local service provided by the Connection.
*
* @see javax.bluetooth.RemoteDevice#authorize(javax.microedition.io.Connection)
*/
public boolean authorize(Connection conn) throws IOException {
if (!(conn instanceof BluetoothConnectionAccess)) {
throw new IllegalArgumentException("Connection is not a Bluetooth connection");
}
if (((BluetoothConnectionAccess) conn).isClosed()) {
throw new IOException("Connection is already closed");
}
if (!(conn instanceof BluetoothServerConnection)) {
throw new IllegalArgumentException("Connection is not an incomming Bluetooth connection");
}
return isTrustedDevice() || isAuthenticated();
}
/**
*
* @see javax.bluetooth.RemoteDevice#isAuthorized(javax.microedition.io.Connection)
*/
public boolean isAuthorized(Connection conn) throws IOException {
if (!(conn instanceof BluetoothConnectionAccess)) {
throw new IllegalArgumentException("Connection is not a Bluetooth connection");
}
if (((BluetoothConnectionAccess) conn).isClosed()) {
throw new IOException("Connection is already closed");
}
if (!(conn instanceof BluetoothServerConnection)) {
throw new IllegalArgumentException("Connection is not an incomming Bluetooth connection");
}
return isTrustedDevice();
}
/**
* Attempts to turn encryption on or off for an existing connection.
*
* @see javax.bluetooth.RemoteDevice#encrypt(javax.microedition.io.Connection,
* boolean)
*/
public boolean encrypt(Connection conn, boolean on) throws IOException {
if (!(conn instanceof BluetoothConnectionAccess)) {
throw new IllegalArgumentException("Connection is not a Bluetooth connection");
}
if (((BluetoothConnectionAccess) conn).getRemoteAddress() != this.addressLong) {
throw new IllegalArgumentException("Connection is not to this device");
}
if ((((BluetoothConnectionAccess) conn).getSecurityOpt() == ServiceRecord.AUTHENTICATE_ENCRYPT) == on) {
return true;
}
return ((BluetoothConnectionAccess) conn).encrypt(this.addressLong, on);
}
/*
* (non-Javadoc)
*
* @see javax.bluetooth.RemoteDevice#isAuthenticated()
*/
public boolean isAuthenticated() {
if (!hasConnections()) {
DebugLog.debug("no connections, Authenticated = false");
return false;
}
synchronized (connections) {
// Find first authenticated connection
for (Enumeration en = connections.elements(); en.hasMoreElements();) {
BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
if (c.getSecurityOpt() != ServiceRecord.NOAUTHENTICATE_NOENCRYPT) {
return true;
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see javax.bluetooth.RemoteDevice#isEncrypted()
*/
public boolean isEncrypted() {
if (!hasConnections()) {
return false;
}
synchronized (connections) {
// Find first encrypted connection
for (Enumeration en = connections.elements(); en.hasMoreElements();) {
BluetoothConnectionAccess c = (BluetoothConnectionAccess) en.nextElement();
if (c.getSecurityOpt() == ServiceRecord.AUTHENTICATE_ENCRYPT) {
return true;
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see javax.bluetooth.RemoteDevice#isTrustedDevice()
*/
public boolean isTrustedDevice() {
return paired;
}
}
private static Hashtable stackDevicesCashed = new Hashtable();
private RemoteDeviceHelper() {
}
private static synchronized Hashtable devicesCashed(BluetoothStack bluetoothStack) {
Hashtable devicesCashed = (Hashtable) stackDevicesCashed.get(bluetoothStack);
if (devicesCashed == null) {
devicesCashed = new Hashtable();
stackDevicesCashed.put(bluetoothStack, devicesCashed);
}
return devicesCashed;
}
private static RemoteDeviceWithExtendedInfo getCashedDeviceWithExtendedInfo(BluetoothStack bluetoothStack,
long address) {
Object key = new Long(address);
return (RemoteDeviceWithExtendedInfo) devicesCashed(bluetoothStack).get(key);
}
static RemoteDevice getCashedDevice(BluetoothStack bluetoothStack, long address) {
return getCashedDeviceWithExtendedInfo(bluetoothStack, address);
}
static RemoteDevice createRemoteDevice(BluetoothStack bluetoothStack, long address, String name, boolean paired) {
RemoteDeviceWithExtendedInfo dev = getCashedDeviceWithExtendedInfo(bluetoothStack, address);
if (dev == null) {
Object saveID = BlueCoveImpl.getCurrentThreadBluetoothStackID();
try {
BlueCoveImpl.setThreadBluetoothStack(bluetoothStack);
dev = new RemoteDeviceWithExtendedInfo(bluetoothStack, address, name);
} finally {
if (saveID != null) {
BlueCoveImpl.setThreadBluetoothStackID(saveID);
}
}
devicesCashed(bluetoothStack).put(new Long(address), dev);
DebugLog.debug0x("new devicesCashed", address);
} else if (!Utils.isStringSet(dev.name)) {
// New name found
dev.name = name;
} else if (Utils.isStringSet(name)) {
// Update name if changed
dev.name = name;
}
if (paired) {
dev.paired = paired;
}
return dev;
}
private static BluetoothStack getBluetoothStack() throws RuntimeException {
try {
return BlueCoveImpl.instance().getBluetoothStack();
} catch (BluetoothStateException e) {
throw (RuntimeException) UtilsJavaSE.initCause(new RuntimeException("Can't initialize bluetooth support"),
e);
}
}
private static RemoteDeviceWithExtendedInfo remoteDeviceImpl(RemoteDevice device) {
return (RemoteDeviceWithExtendedInfo) createRemoteDevice(null, device);
}
static RemoteDevice createRemoteDevice(BluetoothStack bluetoothStack, RemoteDevice device) throws RuntimeException {
if (device instanceof RemoteDeviceWithExtendedInfo) {
return device;
} else {
if (bluetoothStack == null) {
bluetoothStack = getBluetoothStack();
}
return createRemoteDevice(bluetoothStack, getAddress(device), null, false);
}
}
public static String getFriendlyName(RemoteDevice device, long address, boolean alwaysAsk) throws IOException {
String name = null;
if (!(device instanceof RemoteDeviceWithExtendedInfo)) {
device = createRemoteDevice(null, device);
}
name = ((RemoteDeviceWithExtendedInfo) device).name;
if (alwaysAsk || (!Utils.isStringSet(name))) {
name = ((RemoteDeviceWithExtendedInfo) device).bluetoothStack.getRemoteDeviceFriendlyName(address);
if (Utils.isStringSet(name)) {
((RemoteDeviceWithExtendedInfo) device).name = name;
} else {
throw new IOException("Can't query remote device");
}
}
return name;
}
/**
* @see javax.bluetooth.RemoteDevice#getRemoteDevice(Connection)
*/
public static RemoteDevice getRemoteDevice(Connection conn) throws IOException {
if (!(conn instanceof BluetoothConnectionAccess)) {
throw new IllegalArgumentException("Not a Bluetooth connection " + conn.getClass().getName());
}
return createRemoteDevice(((BluetoothConnectionAccess) conn).getBluetoothStack(),
((BluetoothConnectionAccess) conn).getRemoteAddress(), null, false);
}
/**
* (non-Javadoc)
*
* @see javax.bluetooth.DiscoveryAgent#retrieveDevices(int)
*/
public static RemoteDevice[] retrieveDevices(BluetoothStack bluetoothStack, int option) {
Hashtable devicesCashed = devicesCashed(bluetoothStack);
switch (option) {
case DiscoveryAgent.PREKNOWN:
if (devicesCashed.size() == 0) {
// Spec: null if no devices meet the criteria
return null;
}
Vector devicesPaired = new Vector();
for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
RemoteDeviceWithExtendedInfo d = (RemoteDeviceWithExtendedInfo) en.nextElement();
if (d.isTrustedDevice()) {
devicesPaired.addElement(d);
}
}
if (devicesPaired.size() == 0) {
// Spec: null if no devices meet the criteria
return null;
}
RemoteDevice[] pdevices = new RemoteDevice[devicesPaired.size()];
int i = 0;
for (Enumeration en = devicesPaired.elements(); en.hasMoreElements();) {
pdevices[i++] = (RemoteDevice) en.nextElement();
}
return pdevices;
case DiscoveryAgent.CACHED:
if (devicesCashed.size() == 0) {
// Spec: null if no devices meet the criteria
return null;
}
RemoteDevice[] devices = new RemoteDevice[devicesCashed.size()];
int k = 0;
for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
devices[k++] = (RemoteDevice) en.nextElement();
}
return devices;
default:
throw new IllegalArgumentException("invalid option");
}
}
/**
* Count total number of open connections to all devices.
*
* @return number of connections
*/
public static int openConnections() {
int c = 0;
Hashtable devicesCashed = devicesCashed(getBluetoothStack());
synchronized (devicesCashed) {
for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
c += ((RemoteDeviceWithExtendedInfo) en.nextElement()).connectionsCount();
}
}
return c;
}
/**
* Count number of open connections to or from specific device.
*
* @return number of connections
*/
public static int openConnections(long address) {
RemoteDeviceWithExtendedInfo dev = getCashedDeviceWithExtendedInfo(getBluetoothStack(), address);
if (dev == null) {
return 0;
}
return dev.connectionsCount();
}
/**
* Count number of device that have open connections to or from them.
*
* @return number of connections
*/
public static int connectedDevices() {
int c = 0;
Hashtable devicesCashed = devicesCashed(getBluetoothStack());
synchronized (devicesCashed) {
for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
if (((RemoteDeviceWithExtendedInfo) en.nextElement()).hasConnections()) {
c++;
}
}
}
return c;
}
static void shutdownConnections(BluetoothStack bluetoothStack) {
Hashtable devicesCashed = devicesCashed(bluetoothStack);
synchronized (devicesCashed) {
for (Enumeration en = devicesCashed.elements(); en.hasMoreElements();) {
((RemoteDeviceWithExtendedInfo) en.nextElement()).shutdownConnections();
}
}
}
public static String formatBluetoothAddress(String address) {
String s = address.toUpperCase();
return "000000000000".substring(s.length()) + s;
}
public static String getBluetoothAddress(long address) {
return formatBluetoothAddress(Utils.toHexString(address));
}
public static long getAddress(String bluetoothAddress) {
if (bluetoothAddress.indexOf('-') != -1) {
throw new IllegalArgumentException("Illegal bluetoothAddress {" + bluetoothAddress + "}");
}
try {
return Long.parseLong(bluetoothAddress, 16);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Illegal bluetoothAddress {" + bluetoothAddress + "}");
}
}
static long getAddress(RemoteDevice device) {
if (device instanceof RemoteDeviceWithExtendedInfo) {
return ((RemoteDeviceWithExtendedInfo) device).addressLong;
} else {
return getAddress(device.getBluetoothAddress());
}
}
static void setStackAttributes(BluetoothStack bluetoothStack, RemoteDevice device, Object key, Object value) {
RemoteDeviceWithExtendedInfo devInfo = (RemoteDeviceWithExtendedInfo) createRemoteDevice(bluetoothStack, device);
devInfo.setStackAttributes(key, value);
}
static Object getStackAttributes(BluetoothStack bluetoothStack, RemoteDevice device, Object key) {
RemoteDeviceWithExtendedInfo devInfo = null;
if (device instanceof RemoteDeviceWithExtendedInfo) {
devInfo = (RemoteDeviceWithExtendedInfo) device;
} else {
devInfo = getCashedDeviceWithExtendedInfo(bluetoothStack, getAddress(device));
}
if (devInfo != null) {
return devInfo.getStackAttributes(key);
} else {
return null;
}
}
static void connected(BluetoothConnectionAccess connection) throws IOException {
RemoteDeviceWithExtendedInfo device = (RemoteDeviceWithExtendedInfo) getRemoteDevice((Connection) connection);
connection.setRemoteDevice(device);
device.addConnection(connection);
}
static void disconnected(BluetoothConnectionAccess connection) {
RemoteDevice d = connection.getRemoteDevice();
if (d != null) {
((RemoteDeviceWithExtendedInfo) d).removeConnection(connection);
connection.setRemoteDevice(null);
}
}
/**
* Attempts to authenticate RemoteDevice. Return false
if the
* stack does not support authentication.
*
* @see javax.bluetooth.RemoteDevice#authenticate()
*/
public static boolean authenticate(RemoteDevice device) throws IOException {
return remoteDeviceImpl(device).authenticate();
}
/**
*
* Sends an authentication request to a remote Bluetooth device. Non JSR-82,
* Return false
if the stack does not support authentication.
*
* PUBLIC JSR-82 extension
*
* @param device
* Remote Device
* @param passkey
* A Personal Identification Number (PIN) to be used for device
* authentication.
* @return true
if authentication is successful; otherwise
* false
* @throws IOException
* if there are error during authentication.
*/
public static boolean authenticate(RemoteDevice device, String passkey) throws IOException {
return remoteDeviceImpl(device).authenticate(passkey);
}
/**
* Determines if this RemoteDevice should be allowed to continue to access
* the local service provided by the Connection.
*
* @see javax.bluetooth.RemoteDevice#authorize(javax.microedition.io.Connection)
*/
public static boolean authorize(RemoteDevice device, Connection conn) throws IOException {
return remoteDeviceImpl(device).authorize(conn);
}
/**
* Attempts to turn encryption on or off for an existing connection.
*
* @see javax.bluetooth.RemoteDevice#encrypt(javax.microedition.io.Connection,
* boolean)
*/
public static boolean encrypt(RemoteDevice device, Connection conn, boolean on) throws IOException {
return remoteDeviceImpl(device).encrypt(conn, on);
}
/**
* Determines if this RemoteDevice
has been authenticated.
*
* A device may have been authenticated by this application or another
* application. Authentication applies to an ACL link between devices and
* not on a specific L2CAP, RFCOMM, or OBEX connection. Therefore, if
* authenticate()
is performed when an L2CAP connection is
* made to device A, then isAuthenticated()
may return
* true
when tested as part of making an RFCOMM connection to
* device A.
*
* @return true
if this RemoteDevice
has
* previously been authenticated; false
if it has not
* been authenticated or there are no open connections between the
* local device and this RemoteDevice
*/
public static boolean isAuthenticated(RemoteDevice device) {
return remoteDeviceImpl(device).isAuthenticated();
}
public static boolean isAuthorized(RemoteDevice device, Connection conn) throws IOException {
return remoteDeviceImpl(device).isAuthorized(conn);
}
/**
* Determines if data exchanges with this RemoteDevice
are
* currently being encrypted.
*
* Encryption may have been previously turned on by this or another
* application. Encryption applies to an ACL link between devices and not on
* a specific L2CAP, RFCOMM, or OBEX connection. Therefore, if
* encrypt()
is performed with the on
* parameter set to true
when an L2CAP connection is made to
* device A, then isEncrypted()
may return true
* when tested as part of making an RFCOMM connection to device A.
*
* @return true
if data exchanges with this
* RemoteDevice
are being encrypted;
* false
if they are not being encrypted, or there
* are no open connections between the local device and this
* RemoteDevice
*/
public static boolean isEncrypted(RemoteDevice device) {
return remoteDeviceImpl(device).isEncrypted();
}
public static boolean isTrustedDevice(RemoteDevice device) {
return remoteDeviceImpl(device).isTrustedDevice();
}
}