BluetoothDevice.java revision 32d8571f509c392dca732c243e9b2138c15daecf
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.annotation.SdkConstant; 20import android.annotation.SdkConstant.SdkConstantType; 21import android.content.Context; 22import android.os.IBinder; 23import android.os.Parcel; 24import android.os.Parcelable; 25import android.os.RemoteException; 26import android.os.ServiceManager; 27import android.util.Log; 28 29import java.io.IOException; 30import java.io.UnsupportedEncodingException; 31 32/** 33 * Represents a remote Bluetooth device. 34 * 35 * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link 36 * BluetoothDevice}. 37 * 38 * <p>This class is really just a thin wrapper for a Bluetooth hardware 39 * address. Objects of this class are immutable. Operations on this class 40 * are performed on the remote Bluetooth hardware address, using the 41 * {@link BluetoothAdapter} that was used to create this {@link 42 * BluetoothDevice}. 43 */ 44public final class BluetoothDevice implements Parcelable { 45 private static final String TAG = "BluetoothDevice"; 46 47 /** 48 * Sentinel error value for this class. Guaranteed to not equal any other 49 * integer constant in this class. Provided as a convenience for functions 50 * that require a sentinel error value, for example: 51 * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 52 * BluetoothDevice.ERROR)</code> 53 */ 54 public static final int ERROR = Integer.MIN_VALUE; 55 56 /** 57 * Broadcast Action: Remote device discovered. 58 * <p>Sent when a remote device is found during discovery. 59 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 60 * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or 61 * {@link #EXTRA_RSSI} if they are available. 62 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 63 */ 64 // TODO: Change API to not broadcast RSSI if not available (incoming connection) 65 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 66 public static final String ACTION_FOUND = 67 "android.bluetooth.device.action.FOUND"; 68 69 /** 70 * Broadcast Action: Remote device disappeared. 71 * <p>Sent when a remote device that was found in the last discovery is not 72 * found in the current discovery. 73 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 74 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 75 * @hide 76 */ 77 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 78 public static final String ACTION_DISAPPEARED = 79 "android.bluetooth.device.action.DISAPPEARED"; 80 81 /** 82 * Broadcast Action: Bluetooth class of a remote device has changed. 83 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 84 * #EXTRA_CLASS}. 85 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 86 * @see {@link BluetoothClass} 87 */ 88 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 89 public static final String ACTION_CLASS_CHANGED = 90 "android.bluetooth.device.action.CLASS_CHANGED"; 91 92 /** 93 * Broadcast Action: Indicates a low level (ACL) connection has been 94 * established with a remote device. 95 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 96 * <p>ACL connections are managed automatically by the Android Bluetooth 97 * stack. 98 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 99 */ 100 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 101 public static final String ACTION_ACL_CONNECTED = 102 "android.bluetooth.device.action.ACL_CONNECTED"; 103 104 /** 105 * Broadcast Action: Indicates that a low level (ACL) disconnection has 106 * been requested for a remote device, and it will soon be disconnected. 107 * <p>This is useful for graceful disconnection. Applications should use 108 * this intent as a hint to immediately terminate higher level connections 109 * (RFCOMM, L2CAP, or profile connections) to the remote device. 110 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 111 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 112 */ 113 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 114 public static final String ACTION_ACL_DISCONNECT_REQUESTED = 115 "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED"; 116 117 /** 118 * Broadcast Action: Indicates a low level (ACL) disconnection from a 119 * remote device. 120 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 121 * <p>ACL connections are managed automatically by the Android Bluetooth 122 * stack. 123 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 124 */ 125 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 126 public static final String ACTION_ACL_DISCONNECTED = 127 "android.bluetooth.device.action.ACL_DISCONNECTED"; 128 129 /** 130 * Broadcast Action: Indicates the friendly name of a remote device has 131 * been retrieved for the first time, or changed since the last retrieval. 132 * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link 133 * #EXTRA_NAME}. 134 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 135 */ 136 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 137 public static final String ACTION_NAME_CHANGED = 138 "android.bluetooth.device.action.NAME_CHANGED"; 139 140 /** 141 * Broadcast Action: Indicates a change in the bond state of a remote 142 * device. For example, if a device is bonded (paired). 143 * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link 144 * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}. 145 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 146 */ 147 // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also 148 // contain a hidden extra field EXTRA_REASON with the result code. 149 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 150 public static final String ACTION_BOND_STATE_CHANGED = 151 "android.bluetooth.device.action.BOND_STATE_CHANGED"; 152 153 /** 154 * Used as a Parcelable {@link BluetoothDevice} extra field in every intent 155 * broadcast by this class. It contains the {@link BluetoothDevice} that 156 * the intent applies to. 157 */ 158 public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; 159 160 /** 161 * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link 162 * #ACTION_FOUND} intents. It contains the friendly Bluetooth name. 163 */ 164 public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; 165 166 /** 167 * Used as an optional short extra field in {@link #ACTION_FOUND} intents. 168 * Contains the RSSI value of the remote device as reported by the 169 * Bluetooth hardware. 170 */ 171 public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; 172 173 /** 174 * Used as an Parcelable {@link BluetoothClass} extra field in {@link 175 * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. 176 */ 177 public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; 178 179 /** 180 * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. 181 * Contains the bond state of the remote device. 182 * <p>Possible values are: 183 * {@link #BOND_NONE}, 184 * {@link #BOND_BONDING}, 185 * {@link #BOND_BONDED}. 186 */ 187 public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; 188 /** 189 * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. 190 * Contains the previous bond state of the remote device. 191 * <p>Possible values are: 192 * {@link #BOND_NONE}, 193 * {@link #BOND_BONDING}, 194 * {@link #BOND_BONDED}. 195 */ 196 public static final String EXTRA_PREVIOUS_BOND_STATE = 197 "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; 198 /** 199 * Indicates the remote device is not bonded (paired). 200 * <p>There is no shared link key with the remote device, so communication 201 * (if it is allowed at all) will be unauthenticated and unencrypted. 202 */ 203 public static final int BOND_NONE = 10; 204 /** 205 * Indicates bonding (pairing) is in progress with the remote device. 206 */ 207 public static final int BOND_BONDING = 11; 208 /** 209 * Indicates the remote device is bonded (paired). 210 * <p>A shared link keys exists locally for the remote device, so 211 * communication can be authenticated and encrypted. 212 * <p><i>Being bonded (paired) with a remote device does not necessarily 213 * mean the device is currently connected. It just means that the ponding 214 * procedure was compeleted at some earlier time, and the link key is still 215 * stored locally, ready to use on the next connection. 216 * </i> 217 */ 218 public static final int BOND_BONDED = 12; 219 220 /** @hide */ 221 public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; 222 /** @hide */ 223 public static final String EXTRA_PAIRING_VARIANT = 224 "android.bluetooth.device.extra.PAIRING_VARIANT"; 225 /** @hide */ 226 public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY"; 227 228 /** 229 * Broadcast Action: Indicates a failure to retrieve the name of a remote 230 * device. 231 * <p>Always contains the extra field {@link #EXTRA_DEVICE}. 232 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. 233 * @hide 234 */ 235 //TODO: is this actually useful? 236 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 237 public static final String ACTION_NAME_FAILED = 238 "android.bluetooth.device.action.NAME_FAILED"; 239 240 /** @hide */ 241 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 242 public static final String ACTION_PAIRING_REQUEST = 243 "android.bluetooth.device.action.PAIRING_REQUEST"; 244 /** @hide */ 245 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 246 public static final String ACTION_PAIRING_CANCEL = 247 "android.bluetooth.device.action.PAIRING_CANCEL"; 248 249 /** A bond attempt succeeded 250 * @hide */ 251 public static final int BOND_SUCCESS = 0; 252 /** A bond attempt failed because pins did not match, or remote device did 253 * not respond to pin request in time 254 * @hide */ 255 public static final int UNBOND_REASON_AUTH_FAILED = 1; 256 /** A bond attempt failed because the other side explicilty rejected 257 * bonding 258 * @hide */ 259 public static final int UNBOND_REASON_AUTH_REJECTED = 2; 260 /** A bond attempt failed because we canceled the bonding process 261 * @hide */ 262 public static final int UNBOND_REASON_AUTH_CANCELED = 3; 263 /** A bond attempt failed because we could not contact the remote device 264 * @hide */ 265 public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; 266 /** A bond attempt failed because a discovery is in progress 267 * @hide */ 268 public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; 269 /** A bond attempt failed because of authentication timeout 270 * @hide */ 271 public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; 272 /** A bond attempt failed because of repeated attempts 273 * @hide */ 274 public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; 275 /** An existing bond was explicitly revoked 276 * @hide */ 277 public static final int UNBOND_REASON_REMOVED = 8; 278 279 /** The user will be prompted to enter a pin 280 * @hide */ 281 public static final int PAIRING_VARIANT_PIN = 0; 282 /** The user will be prompted to enter a passkey 283 * @hide */ 284 public static final int PAIRING_VARIANT_PASSKEY = 1; 285 /** The user will be prompted to confirm the passkey displayed on the screen 286 * @hide */ 287 public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; 288 /** The user will be prompted to accept or deny the incoming pairing request 289 * @hide */ 290 public static final int PAIRING_VARIANT_CONSENT = 3; 291 /** The user will be prompted to enter the passkey displayed on remote device 292 * @hide */ 293 public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; 294 295 private static IBluetooth sService; /* Guarenteed constant after first object constructed */ 296 297 private final String mAddress; 298 299 /** 300 * Create a new BluetoothDevice 301 * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", 302 * and is validated in this constructor. 303 * @param address valid Bluetooth MAC address 304 * @throws RuntimeException Bluetooth is not available on this platform 305 * @throws IllegalArgumentException address is invalid 306 * @hide 307 */ 308 /*package*/ BluetoothDevice(String address) { 309 synchronized (BluetoothDevice.class) { 310 if (sService == null) { 311 IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE); 312 if (b == null) { 313 throw new RuntimeException("Bluetooth service not available"); 314 } 315 sService = IBluetooth.Stub.asInterface(b); 316 } 317 } 318 319 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 320 throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); 321 } 322 323 mAddress = address; 324 } 325 326 @Override 327 public boolean equals(Object o) { 328 if (o instanceof BluetoothDevice) { 329 return mAddress.equals(((BluetoothDevice)o).getAddress()); 330 } 331 return false; 332 } 333 334 @Override 335 public int hashCode() { 336 return mAddress.hashCode(); 337 } 338 339 /** 340 * Returns a string representation of this BluetoothDevice. 341 * <p>Currently this is the Bluetooth hardware address, for example 342 * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} 343 * if you explicitly require the Bluetooth hardware address in case the 344 * {@link #toString} representation changes in the future. 345 * @return string representation of this BluetoothDevice 346 */ 347 @Override 348 public String toString() { 349 return mAddress; 350 } 351 352 /** @hide */ 353 public int describeContents() { 354 return 0; 355 } 356 357 /** @hide */ 358 public static final Parcelable.Creator<BluetoothDevice> CREATOR = 359 new Parcelable.Creator<BluetoothDevice>() { 360 public BluetoothDevice createFromParcel(Parcel in) { 361 return new BluetoothDevice(in.readString()); 362 } 363 public BluetoothDevice[] newArray(int size) { 364 return new BluetoothDevice[size]; 365 } 366 }; 367 368 /** @hide */ 369 public void writeToParcel(Parcel out, int flags) { 370 out.writeString(mAddress); 371 } 372 373 /** 374 * Returns the hardware address of this BluetoothDevice. 375 * <p> For example, "00:11:22:AA:BB:CC". 376 * @return Bluetooth hardware address as string 377 */ 378 public String getAddress() { 379 return mAddress; 380 } 381 382 /** 383 * Get the friendly Bluetooth name of the remote device. 384 * 385 * <p>The local adapter will automatically retrieve remote names when 386 * performing a device scan, and will cache them. This method just returns 387 * the name for this device from the cache. 388 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} 389 * 390 * @return the Bluetooth name, or null if there was a problem. 391 */ 392 public String getName() { 393 try { 394 return sService.getRemoteName(mAddress); 395 } catch (RemoteException e) {Log.e(TAG, "", e);} 396 return null; 397 } 398 399 /** 400 * Start the bonding (pairing) process with the remote device. 401 * <p>This is an asynchronous call, it will return immediately. Register 402 * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when 403 * the bonding process completes, and its result. 404 * <p>Android system services will handle the necessary user interactions 405 * to confirm and complete the bonding process. 406 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. 407 * 408 * @return false on immediate error, true if bonding will begin 409 */ 410 public boolean createBond() { 411 try { 412 return sService.createBond(mAddress); 413 } catch (RemoteException e) {Log.e(TAG, "", e);} 414 return false; 415 } 416 417 /** 418 * Cancel an in-progress bonding request started with {@link #createBond}. 419 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. 420 * 421 * @return true on sucess, false on error 422 */ 423 public boolean cancelBondProcess() { 424 try { 425 return sService.cancelBondProcess(mAddress); 426 } catch (RemoteException e) {Log.e(TAG, "", e);} 427 return false; 428 } 429 430 /** 431 * Remove bond (pairing) with the remote device. 432 * <p>Delete the link key associated with the remote device, and 433 * immediately terminate connections to that device that require 434 * authentication and encryption. 435 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. 436 * 437 * @return true on sucess, false on error 438 */ 439 public boolean removeBond() { 440 try { 441 return sService.removeBond(mAddress); 442 } catch (RemoteException e) {Log.e(TAG, "", e);} 443 return false; 444 } 445 446 /** 447 * Get the bond state of the remote device. 448 * <p>Possible values for the bond state are: 449 * {@link #BOND_NONE}, 450 * {@link #BOND_BONDING}, 451 * {@link #BOND_BONDED}. 452 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. 453 * 454 * @return the bond state 455 */ 456 public int getBondState() { 457 try { 458 return sService.getBondState(mAddress); 459 } catch (RemoteException e) {Log.e(TAG, "", e);} 460 return BOND_NONE; 461 } 462 463 /** 464 * Get the Bluetooth class of the remote device. 465 * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. 466 * 467 * @return Bluetooth class object, or null on error 468 */ 469 public BluetoothClass getBluetoothClass() { 470 try { 471 int classInt = sService.getRemoteClass(mAddress); 472 if (classInt == BluetoothClass.ERROR) return null; 473 return new BluetoothClass(classInt); 474 } catch (RemoteException e) {Log.e(TAG, "", e);} 475 return null; 476 } 477 478 /** 479 * Get trust state of a remote device. 480 * @hide 481 */ 482 public boolean getTrustState() { 483 try { 484 return sService.getTrustState(mAddress); 485 } catch (RemoteException e) { 486 Log.e(TAG, "", e); 487 } 488 return false; 489 } 490 491 /** 492 * Set trust state for a remote device. 493 * @param value the trust state value (true or false) 494 * @hide 495 */ 496 public boolean setTrust(boolean value) { 497 try { 498 return sService.setTrust(mAddress, value); 499 } catch (RemoteException e) { 500 Log.e(TAG, "", e); 501 } 502 return false; 503 } 504 505 /** @hide */ 506 public String[] getUuids() { 507 try { 508 return sService.getRemoteUuids(mAddress); 509 } catch (RemoteException e) {Log.e(TAG, "", e);} 510 return null; 511 } 512 513 /** @hide */ 514 public int getServiceChannel(String uuid) { 515 try { 516 return sService.getRemoteServiceChannel(mAddress, uuid); 517 } catch (RemoteException e) {Log.e(TAG, "", e);} 518 return BluetoothDevice.ERROR; 519 } 520 521 /** @hide */ 522 public boolean setPin(byte[] pin) { 523 try { 524 return sService.setPin(mAddress, pin); 525 } catch (RemoteException e) {Log.e(TAG, "", e);} 526 return false; 527 } 528 529 /** @hide */ 530 public boolean setPasskey(int passkey) { 531 try { 532 return sService.setPasskey(mAddress, passkey); 533 } catch (RemoteException e) {Log.e(TAG, "", e);} 534 return false; 535 } 536 537 /** @hide */ 538 public boolean setPairingConfirmation(boolean confirm) { 539 try { 540 return sService.setPairingConfirmation(mAddress, confirm); 541 } catch (RemoteException e) {Log.e(TAG, "", e);} 542 return false; 543 } 544 545 /** @hide */ 546 public boolean cancelPairingUserInput() { 547 try { 548 return sService.cancelPairingUserInput(mAddress); 549 } catch (RemoteException e) {Log.e(TAG, "", e);} 550 return false; 551 } 552 553 /** 554 * Create an RFCOMM {@link BluetoothSocket} ready to start a secure 555 * outgoing connection to this remote device. 556 * <p>The remote device will be authenticated and communication on this 557 * socket will be encrypted. 558 * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing 559 * connection. 560 * <p>Valid RFCOMM channels are in range 1 to 30. 561 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} 562 * 563 * @param channel RFCOMM channel to connect to 564 * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection 565 * @throws IOException on error, for example Bluetooth not available, or 566 * insufficient permissions 567 */ 568 public BluetoothSocket createRfcommSocket(int channel) throws IOException { 569 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel); 570 } 571 572 /** 573 * Construct an insecure RFCOMM socket ready to start an outgoing 574 * connection. 575 * Call #connect on the returned #BluetoothSocket to begin the connection. 576 * The remote device will not be authenticated and communication on this 577 * socket will not be encrypted. 578 * @param port remote port 579 * @return An RFCOMM BluetoothSocket 580 * @throws IOException On error, for example Bluetooth not available, or 581 * insufficient permissions. 582 * @hide 583 */ 584 public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { 585 return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port); 586 } 587 588 /** 589 * Construct a SCO socket ready to start an outgoing connection. 590 * Call #connect on the returned #BluetoothSocket to begin the connection. 591 * @return a SCO BluetoothSocket 592 * @throws IOException on error, for example Bluetooth not available, or 593 * insufficient permissions. 594 * @hide 595 */ 596 public BluetoothSocket createScoSocket() throws IOException { 597 return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1); 598 } 599 600 /** 601 * Check that a pin is valid and convert to byte array. 602 * 603 * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. 604 * @param pin pin as java String 605 * @return the pin code as a UTF8 byte array, or null if it is an invalid 606 * Bluetooth pin. 607 * @hide 608 */ 609 public static byte[] convertPinToBytes(String pin) { 610 if (pin == null) { 611 return null; 612 } 613 byte[] pinBytes; 614 try { 615 pinBytes = pin.getBytes("UTF8"); 616 } catch (UnsupportedEncodingException uee) { 617 Log.e(TAG, "UTF8 not supported?!?"); // this should not happen 618 return null; 619 } 620 if (pinBytes.length <= 0 || pinBytes.length > 16) { 621 return null; 622 } 623 return pinBytes; 624 } 625 626} 627