BluetoothDevice.java revision a4433af5ac677be7c1f63447c0cd78829bdee159
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.bluetooth; 18 19import android.content.Context; 20import android.os.IBinder; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.os.RemoteException; 24import android.os.ServiceManager; 25import android.util.Log; 26 27import java.io.IOException; 28import java.io.UnsupportedEncodingException; 29 30/** 31 * Represents a remote Bluetooth device. 32 * 33 * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link 34 * BluetoothDevice}. 35 * 36 * <p>This class is really just a thin wrapper for a Bluetooth hardware 37 * address. Objects of this class are immutable. Operations on this class 38 * are performed on the remote Bluetooth hardware address, using the 39 * {@link BluetoothAdapter} that was used to create this {@link 40 * BluetoothDevice}. 41 * 42 * TODO: unhide more of this class 43 */ 44public final class BluetoothDevice implements Parcelable { 45 private static final String TAG = "BluetoothDevice"; 46 47 /** We do not have a link key for the remote device, and are therefore not 48 * bonded 49 * @hide*/ 50 public static final int BOND_NOT_BONDED = 0; 51 /** We have a link key for the remote device, and are probably bonded. 52 * @hide */ 53 public static final int BOND_BONDED = 1; 54 /** We are currently attempting bonding 55 * @hide */ 56 public static final int BOND_BONDING = 2; 57 58 /** Ask device picker to show all kinds of BT devices. 59 * @hide */ 60 public static final int DEVICE_PICKER_FILTER_TYPE_ALL = 0; 61 /** Ask device picker to show BT devices that support AUDIO profiles. 62 * @hide */ 63 public static final int DEVICE_PICKER_FILTER_TYPE_AUDIO = 1; 64 /** Ask device picker to show BT devices that support Object Transfer. 65 * @hide */ 66 public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2; 67 68 //TODO: Unify these result codes in BluetoothResult or BluetoothError 69 /** A bond attempt failed because pins did not match, or remote device did 70 * not respond to pin request in time 71 * @hide */ 72 public static final int UNBOND_REASON_AUTH_FAILED = 1; 73 /** A bond attempt failed because the other side explicilty rejected 74 * bonding 75 * @hide */ 76 public static final int UNBOND_REASON_AUTH_REJECTED = 2; 77 /** A bond attempt failed because we canceled the bonding process 78 * @hide */ 79 public static final int UNBOND_REASON_AUTH_CANCELED = 3; 80 /** A bond attempt failed because we could not contact the remote device 81 * @hide */ 82 public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; 83 /** A bond attempt failed because a discovery is in progress 84 * @hide */ 85 public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; 86 /** An existing bond was explicitly revoked 87 * @hide */ 88 public static final int UNBOND_REASON_REMOVED = 6; 89 90 //TODO: Remove duplicates between here and BluetoothAdapter 91 /** The user will be prompted to enter a pin 92 * @hide */ 93 public static final int PAIRING_VARIANT_PIN = 0; 94 /** The user will be prompted to enter a passkey 95 * @hide */ 96 public static final int PAIRING_VARIANT_PASSKEY = 1; 97 /** The user will be prompted to confirm the passkey displayed on the screen 98 * @hide */ 99 public static final int PAIRING_VARIANT_CONFIRMATION = 2; 100 101 private static final int ADDRESS_LENGTH = 17; 102 103 private static IBluetooth sService; /* Guarenteed constant after first object constructed */ 104 105 private final String mAddress; 106 107 /** 108 * Create a new BluetoothDevice 109 * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", 110 * and is validated in this constructor. 111 * @param address valid Bluetooth MAC address 112 * @throws RuntimeException Bluetooth is not available on this platform 113 * @throws IllegalArgumentException address is invalid 114 * @hide 115 */ 116 /*package*/ BluetoothDevice(String address) { 117 synchronized (BluetoothDevice.class) { 118 if (sService == null) { 119 IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE); 120 if (b == null) { 121 throw new RuntimeException("Bluetooth service not available"); 122 } 123 sService = IBluetooth.Stub.asInterface(b); 124 } 125 } 126 127 if (!checkBluetoothAddress(address)) { 128 throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); 129 } 130 131 mAddress = address; 132 } 133 134 @Override 135 public boolean equals(Object o) { 136 if (o instanceof BluetoothDevice) { 137 return mAddress.equals(((BluetoothDevice)o).getAddress()); 138 } 139 return false; 140 } 141 142 @Override 143 public int hashCode() { 144 return mAddress.hashCode(); 145 } 146 147 /** 148 * Returns a string representation of this BluetoothDevice. 149 * <p>Currently this is the Bluetooth hardware address, for example 150 * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} 151 * if you explicitly require the Bluetooth hardware address in case the 152 * {@link #toString} representation changes in the future. 153 * @return string representation of this BluetoothDevice 154 */ 155 @Override 156 public String toString() { 157 return mAddress; 158 } 159 160 /** @hide */ 161 public int describeContents() { 162 return 0; 163 } 164 165 /** @hide */ 166 public static final Parcelable.Creator<BluetoothDevice> CREATOR = 167 new Parcelable.Creator<BluetoothDevice>() { 168 public BluetoothDevice createFromParcel(Parcel in) { 169 return new BluetoothDevice(in.readString()); 170 } 171 public BluetoothDevice[] newArray(int size) { 172 return new BluetoothDevice[size]; 173 } 174 }; 175 176 /** @hide */ 177 public void writeToParcel(Parcel out, int flags) { 178 out.writeString(mAddress); 179 } 180 181 /** 182 * Returns the hardware address of this BluetoothDevice. 183 * <p> For example, "00:11:22:AA:BB:CC". 184 * @return Bluetooth hardware address as string 185 */ 186 public String getAddress() { 187 return mAddress; 188 } 189 190 /** 191 * Get the friendly Bluetooth name of the remote device. 192 * 193 * <p>The local adapter will automatically retrieve remote names when 194 * performing a device scan, and will cache them. This method just returns 195 * the name for this device from the cache. 196 * 197 * @return the Bluetooth name, or null if there was a problem. 198 * @hide 199 */ 200 public String getName() { 201 try { 202 return sService.getRemoteName(mAddress); 203 } catch (RemoteException e) {Log.e(TAG, "", e);} 204 return null; 205 } 206 207 /** 208 * Create a bonding with a remote bluetooth device. 209 * 210 * This is an asynchronous call. The result of this bonding attempt can be 211 * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. 212 * 213 * @param address the remote device Bluetooth address. 214 * @return false If there was an immediate problem creating the bonding, 215 * true otherwise. 216 * @hide 217 */ 218 public boolean createBond() { 219 try { 220 return sService.createBond(mAddress); 221 } catch (RemoteException e) {Log.e(TAG, "", e);} 222 return false; 223 } 224 225 /** 226 * Cancel an in-progress bonding request started with createBond. 227 * @hide 228 */ 229 public boolean cancelBondProcess() { 230 try { 231 return sService.cancelBondProcess(mAddress); 232 } catch (RemoteException e) {Log.e(TAG, "", e);} 233 return false; 234 } 235 236 /** 237 * Removes the remote device and the pairing information associated 238 * with it. 239 * 240 * @return true if the device was disconnected, false otherwise and on 241 * error. 242 * @hide 243 */ 244 public boolean removeBond() { 245 try { 246 return sService.removeBond(mAddress); 247 } catch (RemoteException e) {Log.e(TAG, "", e);} 248 return false; 249 } 250 251 /** 252 * Get the bonding state of a remote device. 253 * 254 * Result is one of: 255 * BluetoothError.* 256 * BOND_* 257 * 258 * @param address Bluetooth hardware address of the remote device to check. 259 * @return Result code 260 * @hide 261 */ 262 public int getBondState() { 263 try { 264 return sService.getBondState(mAddress); 265 } catch (RemoteException e) {Log.e(TAG, "", e);} 266 return BluetoothError.ERROR_IPC; 267 } 268 269 /** @hide */ 270 public int getBluetoothClass() { 271 try { 272 return sService.getRemoteClass(mAddress); 273 } catch (RemoteException e) {Log.e(TAG, "", e);} 274 return BluetoothError.ERROR_IPC; 275 } 276 277 /** @hide */ 278 public String[] getUuids() { 279 try { 280 return sService.getRemoteUuids(mAddress); 281 } catch (RemoteException e) {Log.e(TAG, "", e);} 282 return null; 283 } 284 285 /** @hide */ 286 public int getServiceChannel(String uuid) { 287 try { 288 return sService.getRemoteServiceChannel(mAddress, uuid); 289 } catch (RemoteException e) {Log.e(TAG, "", e);} 290 return BluetoothError.ERROR_IPC; 291 } 292 293 /** @hide */ 294 public boolean setPin(byte[] pin) { 295 try { 296 return sService.setPin(mAddress, pin); 297 } catch (RemoteException e) {Log.e(TAG, "", e);} 298 return false; 299 } 300 301 /** @hide */ 302 public boolean setPasskey(int passkey) { 303 try { 304 return sService.setPasskey(mAddress, passkey); 305 } catch (RemoteException e) {Log.e(TAG, "", e);} 306 return false; 307 } 308 309 /** @hide */ 310 public boolean setPairingConfirmation(boolean confirm) { 311 try { 312 return sService.setPairingConfirmation(mAddress, confirm); 313 } catch (RemoteException e) {Log.e(TAG, "", e);} 314 return false; 315 } 316 317 /** @hide */ 318 public boolean cancelPairingUserInput() { 319 try { 320 return sService.cancelPairingUserInput(mAddress); 321 } catch (RemoteException e) {Log.e(TAG, "", e);} 322 return false; 323 } 324 325 /** 326 * Create an RFCOMM {@link BluetoothSocket} ready to start a secure 327 * outgoing connection to this remote device. 328 * <p>The remote device will be authenticated and communication on this 329 * socket will be encrypted. 330 * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing 331 * connection. 332 * <p>Valid RFCOMM channels are in range 1 to 30. 333 * @param channel RFCOMM channel to connect to 334 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 335 * @throws IOException on error, for example Bluetooth not available, or 336 * insufficient permissions 337 */ 338 public BluetoothSocket createRfcommSocket(int channel) throws IOException { 339 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel); 340 } 341 342 /** 343 * Construct an insecure RFCOMM socket ready to start an outgoing 344 * connection. 345 * Call #connect on the returned #BluetoothSocket to begin the connection. 346 * The remote device will not be authenticated and communication on this 347 * socket will not be encrypted. 348 * @param port remote port 349 * @return An RFCOMM BluetoothSocket 350 * @throws IOException On error, for example Bluetooth not available, or 351 * insufficient permissions. 352 * @hide 353 */ 354 public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { 355 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port); 356 } 357 358 /** 359 * Construct a SCO socket ready to start an outgoing connection. 360 * Call #connect on the returned #BluetoothSocket to begin the connection. 361 * @return a SCO BluetoothSocket 362 * @throws IOException on error, for example Bluetooth not available, or 363 * insufficient permissions. 364 * @hide 365 */ 366 public BluetoothSocket createScoSocket() throws IOException { 367 return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1); 368 } 369 370 /** 371 * Check that a pin is valid and convert to byte array. 372 * 373 * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. 374 * @param pin pin as java String 375 * @return the pin code as a UTF8 byte array, or null if it is an invalid 376 * Bluetooth pin. 377 * @hide 378 */ 379 public static byte[] convertPinToBytes(String pin) { 380 if (pin == null) { 381 return null; 382 } 383 byte[] pinBytes; 384 try { 385 pinBytes = pin.getBytes("UTF8"); 386 } catch (UnsupportedEncodingException uee) { 387 Log.e(TAG, "UTF8 not supported?!?"); // this should not happen 388 return null; 389 } 390 if (pinBytes.length <= 0 || pinBytes.length > 16) { 391 return null; 392 } 393 return pinBytes; 394 } 395 396 /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" 397 * @hide */ 398 public static boolean checkBluetoothAddress(String address) { 399 if (address == null || address.length() != ADDRESS_LENGTH) { 400 return false; 401 } 402 for (int i = 0; i < ADDRESS_LENGTH; i++) { 403 char c = address.charAt(i); 404 switch (i % 3) { 405 case 0: 406 case 1: 407 if (Character.digit(c, 16) != -1) { 408 break; // hex character, OK 409 } 410 return false; 411 case 2: 412 if (c == ':') { 413 break; // OK 414 } 415 return false; 416 } 417 } 418 return true; 419 } 420} 421