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