blob: d613e1cfba27e3d444aa9b8eaf0fc2a177027203 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import android.os.RemoteException;
import android.util.Log;
import java.io.UnsupportedEncodingException;
/**
* The Android Bluetooth API is not finalized, and *will* change. Use at your
* own risk.
*
* Manages the local Bluetooth device. Scan for devices, create bondings,
* power up and down the adapter.
*
* @hide
*/
public class BluetoothDevice {
public static final int MODE_UNKNOWN = -1;
public static final int MODE_OFF = 0;
public static final int MODE_CONNECTABLE = 1;
public static final int MODE_DISCOVERABLE = 2;
public static final int RESULT_FAILURE = -1;
public static final int RESULT_SUCCESS = 0;
/** We do not have a link key for the remote device, and are therefore not
* bonded */
public static final int BOND_NOT_BONDED = 0;
/** We have a link key for the remote device, and are probably bonded. */
public static final int BOND_BONDED = 1;
/** We are currently attempting bonding */
public static final int BOND_BONDING = 2;
//TODO: Unify these result codes in BluetoothResult or BluetoothError
/** A bond attempt failed because pins did not match, or remote device did
* not respond to pin request in time */
public static final int UNBOND_REASON_AUTH_FAILED = 1;
/** A bond attempt failed because the other side explicilty rejected
* bonding */
public static final int UNBOND_REASON_AUTH_REJECTED = 2;
/** A bond attempt failed because we cancelled the bonding process */
public static final int UNBOND_REASON_CANCELLED = 3;
/** A bond attempt failed because we could not contact the remote device */
public static final int UNBOND_REASON_AUTH_REMOTE_DEVICE_DOWN = 4;
/** An existing bond was explicitly revoked */
public static final int UNBOND_REASON_REMOVED = 5;
private static final String TAG = "BluetoothDevice";
private final IBluetoothDevice mService;
/**
* @hide - hide this because it takes a parameter of type
* IBluetoothDevice, which is a System private class.
* Also note that Context.getSystemService is a factory that
* returns a BlueToothDevice. That is the right way to get
* a BluetoothDevice.
*/
public BluetoothDevice(IBluetoothDevice service) {
mService = service;
}
/**
* Get the current status of Bluetooth hardware.
*
* @return true if Bluetooth enabled, false otherwise.
*/
public boolean isEnabled() {
try {
return mService.isEnabled();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Enable the Bluetooth device.
* Turn on the underlying hardware.
* This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be
* sent if and when the device is successfully enabled.
* @return false if we cannot enable the Bluetooth device. True does not
* imply the device was enabled, it only implies that so far there were no
* problems.
*/
public boolean enable() {
return enable(null);
}
/**
* Enable the Bluetooth device.
* Turns on the underlying hardware.
* This is an asynchronous call. onEnableResult() of your callback will be
* called when the call is complete, with either RESULT_SUCCESS or
* RESULT_FAILURE.
*
* Your callback will be called from a binder thread, not the main thread.
*
* In addition to the callback, BluetoothIntent.ENABLED_ACTION will be
* broadcast if the device is successfully enabled.
*
* @param callback Your callback, null is ok.
* @return true if your callback was successfully registered, or false if
* there was an error, implying your callback will never be called.
*/
public boolean enable(IBluetoothDeviceCallback callback) {
try {
return mService.enable(callback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Disable the Bluetooth device.
* This turns off the underlying hardware.
*
* @return true if successful, false otherwise.
*/
public boolean disable() {
try {
return mService.disable();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public String getAddress() {
try {
return mService.getAddress();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Get the friendly Bluetooth name of this device.
*
* This name is visible to remote Bluetooth devices. Currently it is only
* possible to retrieve the Bluetooth name when Bluetooth is enabled.
*
* @return the Bluetooth name, or null if there was a problem.
*/
public String getName() {
try {
return mService.getName();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Set the friendly Bluetooth name of this device.
*
* This name is visible to remote Bluetooth devices. The Bluetooth Service
* is responsible for persisting this name.
*
* @param name the name to set
* @return true, if the name was successfully set. False otherwise.
*/
public boolean setName(String name) {
try {
return mService.setName(name);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public String getMajorClass() {
try {
return mService.getMajorClass();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getMinorClass() {
try {
return mService.getMinorClass();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getVersion() {
try {
return mService.getVersion();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRevision() {
try {
return mService.getRevision();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getManufacturer() {
try {
return mService.getManufacturer();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getCompany() {
try {
return mService.getCompany();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public int getMode() {
try {
return mService.getMode();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return MODE_UNKNOWN;
}
public void setMode(int mode) {
try {
mService.setMode(mode);
} catch (RemoteException e) {Log.e(TAG, "", e);}
}
public int getDiscoverableTimeout() {
try {
return mService.getDiscoverableTimeout();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return -1;
}
public void setDiscoverableTimeout(int timeout) {
try {
mService.setDiscoverableTimeout(timeout);
} catch (RemoteException e) {Log.e(TAG, "", e);}
}
public boolean startDiscovery() {
return startDiscovery(true);
}
public boolean startDiscovery(boolean resolveNames) {
try {
return mService.startDiscovery(resolveNames);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public void cancelDiscovery() {
try {
mService.cancelDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
}
public boolean isDiscovering() {
try {
return mService.isDiscovering();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean startPeriodicDiscovery() {
try {
return mService.startPeriodicDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean stopPeriodicDiscovery() {
try {
return mService.stopPeriodicDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean isPeriodicDiscovery() {
try {
return mService.isPeriodicDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public String[] listRemoteDevices() {
try {
return mService.listRemoteDevices();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* List remote devices that have a low level (ACL) connection.
*
* RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
* an ACL connection even when not paired - this is common for SDP queries
* or for in-progress pairing requests.
*
* In most cases you probably want to test if a higher level protocol is
* connected, rather than testing ACL connections.
*
* @return bluetooth hardware addresses of remote devices with a current
* ACL connection. Array size is 0 if no devices have a
* connection. Null on error.
*/
public String[] listAclConnections() {
try {
return mService.listAclConnections();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Check if a specified remote device has a low level (ACL) connection.
*
* RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
* an ACL connection even when not paired - this is common for SDP queries
* or for in-progress pairing requests.
*
* In most cases you probably want to test if a higher level protocol is
* connected, rather than testing ACL connections.
*
* @param address the Bluetooth hardware address you want to check.
* @return true if there is an ACL connection, false otherwise and on
* error.
*/
public boolean isAclConnected(String address) {
try {
return mService.isAclConnected(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Perform a low level (ACL) disconnection of a remote device.
*
* This forcably disconnects the ACL layer connection to a remote device,
* which will cause all RFCOMM, SDP and L2CAP connections to this remote
* device to close.
*
* @param address the Bluetooth hardware address you want to disconnect.
* @return true if the device was disconnected, false otherwise and on
* error.
*/
public boolean disconnectRemoteDeviceAcl(String address) {
try {
return mService.disconnectRemoteDeviceAcl(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Create a bonding with a remote bluetooth device.
*
* This is an asynchronous call. The result of this bonding attempt can be
* observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents.
*
* @param address the remote device Bluetooth address.
* @return false If there was an immediate problem creating the bonding,
* true otherwise.
*/
public boolean createBond(String address) {
try {
return mService.createBond(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Cancel an in-progress bonding request started with createBond.
*/
public boolean cancelBondProcess(String address) {
try {
return mService.cancelBondProcess(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Remove an already exisiting bonding (delete the link key).
*/
public boolean removeBond(String address) {
try {
return mService.removeBond(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* List remote devices that are bonded (paired) to the local device.
*
* Bonding (pairing) is the process by which the user enters a pin code for
* the device, which generates a shared link key, allowing for
* authentication and encryption of future connections. In Android we
* require bonding before RFCOMM or SCO connections can be made to a remote
* device.
*
* This function lists which remote devices we have a link key for. It does
* not cause any RF transmission, and does not check if the remote device
* still has it's link key with us. If the other side no longer has its
* link key then the RFCOMM or SCO connection attempt will result in an
* error.
*
* This function does not check if the remote device is in range.
*
* Remote devices that have an in-progress bonding attempt are not
* returned.
*
* @return bluetooth hardware addresses of remote devices that are
* bonded. Array size is 0 if no devices are bonded. Null on error.
*/
public String[] listBonds() {
try {
return mService.listBonds();
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Get the bonding state of a remote device.
*
* Result is one of:
* BluetoothError.*
* BOND_*
*
* @param address Bluetooth hardware address of the remote device to check.
* @return Result code
*/
public int getBondState(String address) {
try {
return mService.getBondState(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return BluetoothError.ERROR_IPC;
}
public String getRemoteName(String address) {
try {
return mService.getRemoteName(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteAlias(String address) {
try {
return mService.getRemoteAlias(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public boolean setRemoteAlias(String address, String alias) {
try {
return mService.setRemoteAlias(address, alias);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean clearRemoteAlias(String address) {
try {
return mService.clearRemoteAlias(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public String getRemoteVersion(String address) {
try {
return mService.getRemoteVersion(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteRevision(String address) {
try {
return mService.getRemoteRevision(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteManufacturer(String address) {
try {
return mService.getRemoteManufacturer(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteCompany(String address) {
try {
return mService.getRemoteCompany(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteMajorClass(String address) {
try {
return mService.getRemoteMajorClass(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String getRemoteMinorClass(String address) {
try {
return mService.getRemoteMinorClass(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String[] getRemoteServiceClasses(String address) {
try {
return mService.getRemoteServiceClasses(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
* Returns the RFCOMM channel associated with the 16-byte UUID on
* the remote Bluetooth address.
*
* Performs a SDP ServiceSearchAttributeRequest transaction. The provided
* uuid is verified in the returned record. If there was a problem, or the
* specified uuid does not exist, -1 is returned.
*/
public boolean getRemoteServiceChannel(String address, short uuid16,
IBluetoothDeviceCallback callback) {
try {
return mService.getRemoteServiceChannel(address, uuid16, callback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public int getRemoteClass(String address) {
try {
return mService.getRemoteClass(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return BluetoothClass.ERROR;
}
public byte[] getRemoteFeatures(String address) {
try {
return mService.getRemoteFeatures(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String lastSeen(String address) {
try {
return mService.lastSeen(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public String lastUsed(String address) {
try {
return mService.lastUsed(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
public boolean setPin(String address, byte[] pin) {
try {
return mService.setPin(address, pin);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
public boolean cancelPin(String address) {
try {
return mService.cancelPin(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Check that a pin is valid and convert to byte array.
*
* Bluetooth pin's are 1 to 16 bytes of UTF8 characters.
* @param pin pin as java String
* @return the pin code as a UTF8 byte array, or null if it is an invalid
* Bluetooth pin.
*/
public static byte[] convertPinToBytes(String pin) {
if (pin == null) {
return null;
}
byte[] pinBytes;
try {
pinBytes = pin.getBytes("UTF8");
} catch (UnsupportedEncodingException uee) {
Log.e(TAG, "UTF8 not supported?!?"); // this should not happen
return null;
}
if (pinBytes.length <= 0 || pinBytes.length > 16) {
return null;
}
return pinBytes;
}
/* Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */
private static final int ADDRESS_LENGTH = 17;
public static boolean checkBluetoothAddress(String address) {
if (address == null || address.length() != ADDRESS_LENGTH) {
return false;
}
for (int i = 0; i < ADDRESS_LENGTH; i++) {
char c = address.charAt(i);
switch (i % 3) {
case 0:
case 1:
if (Character.digit(c, 16) != -1) {
break; // hex character, OK
}
return false;
case 2:
if (c == ':') {
break; // OK
}
return false;
}
}
return true;
}
}