BluetoothDevice.java revision 076357b8567458d4b6dfdcf839ef751634cd2bfb
1/* 2 * Copyright (C) 2008 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.os.RemoteException; 20import android.util.Log; 21 22import java.io.UnsupportedEncodingException; 23 24/** 25 * The Android Bluetooth API is not finalized, and *will* change. Use at your 26 * own risk. 27 * 28 * Manages the local Bluetooth device. Scan for devices, create bondings, 29 * power up and down the adapter. 30 * 31 * @hide 32 */ 33public class BluetoothDevice { 34 /** Inquiry scan and page scan are both off. 35 * Device is neither discoverable nor connectable */ 36 public static final int SCAN_MODE_NONE = 0; 37 /** Page scan is on, inquiry scan is off. 38 * Device is connectable, but not discoverable */ 39 public static final int SCAN_MODE_CONNECTABLE = 1; 40 /** Page scan and inquiry scan are on. 41 * Device is connectable and discoverable */ 42 public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3; 43 44 public static final int RESULT_FAILURE = -1; 45 public static final int RESULT_SUCCESS = 0; 46 47 /** We do not have a link key for the remote device, and are therefore not 48 * bonded */ 49 public static final int BOND_NOT_BONDED = 0; 50 /** We have a link key for the remote device, and are probably bonded. */ 51 public static final int BOND_BONDED = 1; 52 /** We are currently attempting bonding */ 53 public static final int BOND_BONDING = 2; 54 55 //TODO: Unify these result codes in BluetoothResult or BluetoothError 56 /** A bond attempt failed because pins did not match, or remote device did 57 * not respond to pin request in time */ 58 public static final int UNBOND_REASON_AUTH_FAILED = 1; 59 /** A bond attempt failed because the other side explicilty rejected 60 * bonding */ 61 public static final int UNBOND_REASON_AUTH_REJECTED = 2; 62 /** A bond attempt failed because we canceled the bonding process */ 63 public static final int UNBOND_REASON_AUTH_CANCELED = 3; 64 /** A bond attempt failed because we could not contact the remote device */ 65 public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; 66 /** An existing bond was explicitly revoked */ 67 public static final int UNBOND_REASON_REMOVED = 5; 68 69 private static final String TAG = "BluetoothDevice"; 70 71 private final IBluetoothDevice mService; 72 /** 73 * @hide - hide this because it takes a parameter of type 74 * IBluetoothDevice, which is a System private class. 75 * Also note that Context.getSystemService is a factory that 76 * returns a BlueToothDevice. That is the right way to get 77 * a BluetoothDevice. 78 */ 79 public BluetoothDevice(IBluetoothDevice service) { 80 mService = service; 81 } 82 83 /** 84 * Get the current status of Bluetooth hardware. 85 * 86 * @return true if Bluetooth enabled, false otherwise. 87 */ 88 public boolean isEnabled() { 89 try { 90 return mService.isEnabled(); 91 } catch (RemoteException e) {Log.e(TAG, "", e);} 92 return false; 93 } 94 95 /** 96 * Enable the Bluetooth device. 97 * Turn on the underlying hardware. 98 * This is an asynchronous call, BluetoothIntent.ENABLED_ACTION will be 99 * sent if and when the device is successfully enabled. 100 * @return false if we cannot enable the Bluetooth device. True does not 101 * imply the device was enabled, it only implies that so far there were no 102 * problems. 103 */ 104 public boolean enable() { 105 return enable(null); 106 } 107 108 /** 109 * Enable the Bluetooth device. 110 * Turns on the underlying hardware. 111 * This is an asynchronous call. onEnableResult() of your callback will be 112 * called when the call is complete, with either RESULT_SUCCESS or 113 * RESULT_FAILURE. 114 * 115 * Your callback will be called from a binder thread, not the main thread. 116 * 117 * In addition to the callback, BluetoothIntent.ENABLED_ACTION will be 118 * broadcast if the device is successfully enabled. 119 * 120 * @param callback Your callback, null is ok. 121 * @return true if your callback was successfully registered, or false if 122 * there was an error, implying your callback will never be called. 123 */ 124 public boolean enable(IBluetoothDeviceCallback callback) { 125 try { 126 return mService.enable(callback); 127 } catch (RemoteException e) {Log.e(TAG, "", e);} 128 return false; 129 } 130 131 /** 132 * Disable the Bluetooth device. 133 * This turns off the underlying hardware. 134 * 135 * @return true if successful, false otherwise. 136 */ 137 public boolean disable() { 138 try { 139 return mService.disable(); 140 } catch (RemoteException e) {Log.e(TAG, "", e);} 141 return false; 142 } 143 144 public String getAddress() { 145 try { 146 return mService.getAddress(); 147 } catch (RemoteException e) {Log.e(TAG, "", e);} 148 return null; 149 } 150 151 /** 152 * Get the friendly Bluetooth name of this device. 153 * 154 * This name is visible to remote Bluetooth devices. Currently it is only 155 * possible to retrieve the Bluetooth name when Bluetooth is enabled. 156 * 157 * @return the Bluetooth name, or null if there was a problem. 158 */ 159 public String getName() { 160 try { 161 return mService.getName(); 162 } catch (RemoteException e) {Log.e(TAG, "", e);} 163 return null; 164 } 165 166 /** 167 * Set the friendly Bluetooth name of this device. 168 * 169 * This name is visible to remote Bluetooth devices. The Bluetooth Service 170 * is responsible for persisting this name. 171 * 172 * @param name the name to set 173 * @return true, if the name was successfully set. False otherwise. 174 */ 175 public boolean setName(String name) { 176 try { 177 return mService.setName(name); 178 } catch (RemoteException e) {Log.e(TAG, "", e);} 179 return false; 180 } 181 182 public String getVersion() { 183 try { 184 return mService.getVersion(); 185 } catch (RemoteException e) {Log.e(TAG, "", e);} 186 return null; 187 } 188 public String getRevision() { 189 try { 190 return mService.getRevision(); 191 } catch (RemoteException e) {Log.e(TAG, "", e);} 192 return null; 193 } 194 public String getManufacturer() { 195 try { 196 return mService.getManufacturer(); 197 } catch (RemoteException e) {Log.e(TAG, "", e);} 198 return null; 199 } 200 public String getCompany() { 201 try { 202 return mService.getCompany(); 203 } catch (RemoteException e) {Log.e(TAG, "", e);} 204 return null; 205 } 206 207 /** 208 * Get the current scan mode. 209 * Used to determine if the local device is connectable and/or discoverable 210 * @return Scan mode, one of SCAN_MODE_* or an error code 211 */ 212 public int getScanMode() { 213 try { 214 return mService.getScanMode(); 215 } catch (RemoteException e) {Log.e(TAG, "", e);} 216 return BluetoothError.ERROR_IPC; 217 } 218 219 /** 220 * Set the current scan mode. 221 * Used to make the local device connectable and/or discoverable 222 * @param scanMode One of SCAN_MODE_* 223 */ 224 public void setScanMode(int scanMode) { 225 try { 226 mService.setScanMode(scanMode); 227 } catch (RemoteException e) {Log.e(TAG, "", e);} 228 } 229 230 public int getDiscoverableTimeout() { 231 try { 232 return mService.getDiscoverableTimeout(); 233 } catch (RemoteException e) {Log.e(TAG, "", e);} 234 return -1; 235 } 236 public void setDiscoverableTimeout(int timeout) { 237 try { 238 mService.setDiscoverableTimeout(timeout); 239 } catch (RemoteException e) {Log.e(TAG, "", e);} 240 } 241 242 public boolean startDiscovery() { 243 return startDiscovery(true); 244 } 245 public boolean startDiscovery(boolean resolveNames) { 246 try { 247 return mService.startDiscovery(resolveNames); 248 } catch (RemoteException e) {Log.e(TAG, "", e);} 249 return false; 250 } 251 252 public void cancelDiscovery() { 253 try { 254 mService.cancelDiscovery(); 255 } catch (RemoteException e) {Log.e(TAG, "", e);} 256 } 257 258 public boolean isDiscovering() { 259 try { 260 return mService.isDiscovering(); 261 } catch (RemoteException e) {Log.e(TAG, "", e);} 262 return false; 263 } 264 265 public boolean startPeriodicDiscovery() { 266 try { 267 return mService.startPeriodicDiscovery(); 268 } catch (RemoteException e) {Log.e(TAG, "", e);} 269 return false; 270 } 271 public boolean stopPeriodicDiscovery() { 272 try { 273 return mService.stopPeriodicDiscovery(); 274 } catch (RemoteException e) {Log.e(TAG, "", e);} 275 return false; 276 } 277 public boolean isPeriodicDiscovery() { 278 try { 279 return mService.isPeriodicDiscovery(); 280 } catch (RemoteException e) {Log.e(TAG, "", e);} 281 return false; 282 } 283 284 public String[] listRemoteDevices() { 285 try { 286 return mService.listRemoteDevices(); 287 } catch (RemoteException e) {Log.e(TAG, "", e);} 288 return null; 289 } 290 291 /** 292 * List remote devices that have a low level (ACL) connection. 293 * 294 * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have 295 * an ACL connection even when not paired - this is common for SDP queries 296 * or for in-progress pairing requests. 297 * 298 * In most cases you probably want to test if a higher level protocol is 299 * connected, rather than testing ACL connections. 300 * 301 * @return bluetooth hardware addresses of remote devices with a current 302 * ACL connection. Array size is 0 if no devices have a 303 * connection. Null on error. 304 */ 305 public String[] listAclConnections() { 306 try { 307 return mService.listAclConnections(); 308 } catch (RemoteException e) {Log.e(TAG, "", e);} 309 return null; 310 } 311 312 /** 313 * Check if a specified remote device has a low level (ACL) connection. 314 * 315 * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have 316 * an ACL connection even when not paired - this is common for SDP queries 317 * or for in-progress pairing requests. 318 * 319 * In most cases you probably want to test if a higher level protocol is 320 * connected, rather than testing ACL connections. 321 * 322 * @param address the Bluetooth hardware address you want to check. 323 * @return true if there is an ACL connection, false otherwise and on 324 * error. 325 */ 326 public boolean isAclConnected(String address) { 327 try { 328 return mService.isAclConnected(address); 329 } catch (RemoteException e) {Log.e(TAG, "", e);} 330 return false; 331 } 332 333 /** 334 * Perform a low level (ACL) disconnection of a remote device. 335 * 336 * This forcably disconnects the ACL layer connection to a remote device, 337 * which will cause all RFCOMM, SDP and L2CAP connections to this remote 338 * device to close. 339 * 340 * @param address the Bluetooth hardware address you want to disconnect. 341 * @return true if the device was disconnected, false otherwise and on 342 * error. 343 */ 344 public boolean disconnectRemoteDeviceAcl(String address) { 345 try { 346 return mService.disconnectRemoteDeviceAcl(address); 347 } catch (RemoteException e) {Log.e(TAG, "", e);} 348 return false; 349 } 350 351 /** 352 * Create a bonding with a remote bluetooth device. 353 * 354 * This is an asynchronous call. The result of this bonding attempt can be 355 * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents. 356 * 357 * @param address the remote device Bluetooth address. 358 * @return false If there was an immediate problem creating the bonding, 359 * true otherwise. 360 */ 361 public boolean createBond(String address) { 362 try { 363 return mService.createBond(address); 364 } catch (RemoteException e) {Log.e(TAG, "", e);} 365 return false; 366 } 367 368 /** 369 * Cancel an in-progress bonding request started with createBond. 370 */ 371 public boolean cancelBondProcess(String address) { 372 try { 373 return mService.cancelBondProcess(address); 374 } catch (RemoteException e) {Log.e(TAG, "", e);} 375 return false; 376 } 377 378 /** 379 * Remove an already exisiting bonding (delete the link key). 380 */ 381 public boolean removeBond(String address) { 382 try { 383 return mService.removeBond(address); 384 } catch (RemoteException e) {Log.e(TAG, "", e);} 385 return false; 386 } 387 388 /** 389 * List remote devices that are bonded (paired) to the local device. 390 * 391 * Bonding (pairing) is the process by which the user enters a pin code for 392 * the device, which generates a shared link key, allowing for 393 * authentication and encryption of future connections. In Android we 394 * require bonding before RFCOMM or SCO connections can be made to a remote 395 * device. 396 * 397 * This function lists which remote devices we have a link key for. It does 398 * not cause any RF transmission, and does not check if the remote device 399 * still has it's link key with us. If the other side no longer has its 400 * link key then the RFCOMM or SCO connection attempt will result in an 401 * error. 402 * 403 * This function does not check if the remote device is in range. 404 * 405 * Remote devices that have an in-progress bonding attempt are not 406 * returned. 407 * 408 * @return bluetooth hardware addresses of remote devices that are 409 * bonded. Array size is 0 if no devices are bonded. Null on error. 410 */ 411 public String[] listBonds() { 412 try { 413 return mService.listBonds(); 414 } catch (RemoteException e) {Log.e(TAG, "", e);} 415 return null; 416 } 417 418 /** 419 * Get the bonding state of a remote device. 420 * 421 * Result is one of: 422 * BluetoothError.* 423 * BOND_* 424 * 425 * @param address Bluetooth hardware address of the remote device to check. 426 * @return Result code 427 */ 428 public int getBondState(String address) { 429 try { 430 return mService.getBondState(address); 431 } catch (RemoteException e) {Log.e(TAG, "", e);} 432 return BluetoothError.ERROR_IPC; 433 } 434 435 public String getRemoteName(String address) { 436 try { 437 return mService.getRemoteName(address); 438 } catch (RemoteException e) {Log.e(TAG, "", e);} 439 return null; 440 } 441 442 public String getRemoteVersion(String address) { 443 try { 444 return mService.getRemoteVersion(address); 445 } catch (RemoteException e) {Log.e(TAG, "", e);} 446 return null; 447 } 448 public String getRemoteRevision(String address) { 449 try { 450 return mService.getRemoteRevision(address); 451 } catch (RemoteException e) {Log.e(TAG, "", e);} 452 return null; 453 } 454 public String getRemoteManufacturer(String address) { 455 try { 456 return mService.getRemoteManufacturer(address); 457 } catch (RemoteException e) {Log.e(TAG, "", e);} 458 return null; 459 } 460 public String getRemoteCompany(String address) { 461 try { 462 return mService.getRemoteCompany(address); 463 } catch (RemoteException e) {Log.e(TAG, "", e);} 464 return null; 465 } 466 467 /** 468 * Returns the RFCOMM channel associated with the 16-byte UUID on 469 * the remote Bluetooth address. 470 * 471 * Performs a SDP ServiceSearchAttributeRequest transaction. The provided 472 * uuid is verified in the returned record. If there was a problem, or the 473 * specified uuid does not exist, -1 is returned. 474 */ 475 public boolean getRemoteServiceChannel(String address, short uuid16, 476 IBluetoothDeviceCallback callback) { 477 try { 478 return mService.getRemoteServiceChannel(address, uuid16, callback); 479 } catch (RemoteException e) {Log.e(TAG, "", e);} 480 return false; 481 } 482 483 /** 484 * Get the major, minor and servics classes of a remote device. 485 * These classes are encoded as a 32-bit integer. See BluetoothClass. 486 * @param address remote device 487 * @return 32-bit class suitable for use with BluetoothClass. 488 */ 489 public int getRemoteClass(String address) { 490 try { 491 return mService.getRemoteClass(address); 492 } catch (RemoteException e) {Log.e(TAG, "", e);} 493 return BluetoothClass.ERROR; 494 } 495 496 public byte[] getRemoteFeatures(String address) { 497 try { 498 return mService.getRemoteFeatures(address); 499 } catch (RemoteException e) {Log.e(TAG, "", e);} 500 return null; 501 } 502 public String lastSeen(String address) { 503 try { 504 return mService.lastSeen(address); 505 } catch (RemoteException e) {Log.e(TAG, "", e);} 506 return null; 507 } 508 public String lastUsed(String address) { 509 try { 510 return mService.lastUsed(address); 511 } catch (RemoteException e) {Log.e(TAG, "", e);} 512 return null; 513 } 514 515 public boolean setPin(String address, byte[] pin) { 516 try { 517 return mService.setPin(address, pin); 518 } catch (RemoteException e) {Log.e(TAG, "", e);} 519 return false; 520 } 521 public boolean cancelPin(String address) { 522 try { 523 return mService.cancelPin(address); 524 } catch (RemoteException e) {Log.e(TAG, "", e);} 525 return false; 526 } 527 528 /** 529 * Check that a pin is valid and convert to byte array. 530 * 531 * Bluetooth pin's are 1 to 16 bytes of UTF8 characters. 532 * @param pin pin as java String 533 * @return the pin code as a UTF8 byte array, or null if it is an invalid 534 * Bluetooth pin. 535 */ 536 public static byte[] convertPinToBytes(String pin) { 537 if (pin == null) { 538 return null; 539 } 540 byte[] pinBytes; 541 try { 542 pinBytes = pin.getBytes("UTF8"); 543 } catch (UnsupportedEncodingException uee) { 544 Log.e(TAG, "UTF8 not supported?!?"); // this should not happen 545 return null; 546 } 547 if (pinBytes.length <= 0 || pinBytes.length > 16) { 548 return null; 549 } 550 return pinBytes; 551 } 552 553 554 private static final int ADDRESS_LENGTH = 17; 555 /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */ 556 public static boolean checkBluetoothAddress(String address) { 557 if (address == null || address.length() != ADDRESS_LENGTH) { 558 return false; 559 } 560 for (int i = 0; i < ADDRESS_LENGTH; i++) { 561 char c = address.charAt(i); 562 switch (i % 3) { 563 case 0: 564 case 1: 565 if (Character.digit(c, 16) != -1) { 566 break; // hex character, OK 567 } 568 return false; 569 case 2: 570 if (c == ':') { 571 break; // OK 572 } 573 return false; 574 } 575 } 576 return true; 577 } 578} 579