1 /* 2 * Copyright (C) 2013 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 17 package android.bluetooth; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothProfile; 21 import android.bluetooth.BluetoothProfile.ServiceListener; 22 import android.bluetooth.IBluetoothManager; 23 import android.bluetooth.IBluetoothStateChangeCallback; 24 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.ServiceConnection; 29 import android.os.IBinder; 30 import android.os.ParcelUuid; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.util.Log; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.UUID; 38 39 /** 40 * Public API for the Bluetooth GATT Profile. 41 * 42 * <p>This class provides Bluetooth GATT functionality to enable communication 43 * with Bluetooth Smart or Smart Ready devices. 44 * 45 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} 46 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. 47 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE 48 * scan process. 49 */ 50 public final class BluetoothGatt implements BluetoothProfile { 51 private static final String TAG = "BluetoothGatt"; 52 private static final boolean DBG = true; 53 private static final boolean VDBG = true; 54 55 private final Context mContext; 56 private IBluetoothGatt mService; 57 private BluetoothGattCallback mCallback; 58 private int mClientIf; 59 private boolean mAuthRetry = false; 60 private BluetoothDevice mDevice; 61 private boolean mAutoConnect; 62 private int mConnState; 63 private final Object mStateLock = new Object(); 64 65 private static final int CONN_STATE_IDLE = 0; 66 private static final int CONN_STATE_CONNECTING = 1; 67 private static final int CONN_STATE_CONNECTED = 2; 68 private static final int CONN_STATE_DISCONNECTING = 3; 69 private static final int CONN_STATE_CLOSED = 4; 70 71 private List<BluetoothGattService> mServices; 72 73 /** A GATT operation completed successfully */ 74 public static final int GATT_SUCCESS = 0; 75 76 /** GATT read operation is not permitted */ 77 public static final int GATT_READ_NOT_PERMITTED = 0x2; 78 79 /** GATT write operation is not permitted */ 80 public static final int GATT_WRITE_NOT_PERMITTED = 0x3; 81 82 /** Insufficient authentication for a given operation */ 83 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5; 84 85 /** The given request is not supported */ 86 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6; 87 88 /** Insufficient encryption for a given operation */ 89 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf; 90 91 /** A read or write operation was requested with an invalid offset */ 92 public static final int GATT_INVALID_OFFSET = 0x7; 93 94 /** A write operation exceeds the maximum length of the attribute */ 95 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; 96 97 /** A GATT operation failed, errors other than the above */ 98 public static final int GATT_FAILURE = 0x101; 99 100 /** 101 * No authentication required. 102 * @hide 103 */ 104 /*package*/ static final int AUTHENTICATION_NONE = 0; 105 106 /** 107 * Authentication requested; no man-in-the-middle protection required. 108 * @hide 109 */ 110 /*package*/ static final int AUTHENTICATION_NO_MITM = 1; 111 112 /** 113 * Authentication with man-in-the-middle protection requested. 114 * @hide 115 */ 116 /*package*/ static final int AUTHENTICATION_MITM = 2; 117 118 /** 119 * Bluetooth GATT interface callbacks 120 */ 121 private final IBluetoothGattCallback mBluetoothGattCallback = 122 new IBluetoothGattCallback.Stub() { 123 /** 124 * Application interface registered - app is ready to go 125 * @hide 126 */ 127 public void onClientRegistered(int status, int clientIf) { 128 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status 129 + " clientIf=" + clientIf); 130 if (VDBG) { 131 synchronized(mStateLock) { 132 if (mConnState != CONN_STATE_CONNECTING) { 133 Log.e(TAG, "Bad connection state: " + mConnState); 134 } 135 } 136 } 137 mClientIf = clientIf; 138 if (status != GATT_SUCCESS) { 139 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, 140 BluetoothProfile.STATE_DISCONNECTED); 141 synchronized(mStateLock) { 142 mConnState = CONN_STATE_IDLE; 143 } 144 return; 145 } 146 try { 147 mService.clientConnect(mClientIf, mDevice.getAddress(), 148 !mAutoConnect); // autoConnect is inverse of "isDirect" 149 } catch (RemoteException e) { 150 Log.e(TAG,"",e); 151 } 152 } 153 154 /** 155 * Client connection state changed 156 * @hide 157 */ 158 public void onClientConnectionState(int status, int clientIf, 159 boolean connected, String address) { 160 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status 161 + " clientIf=" + clientIf + " device=" + address); 162 if (!address.equals(mDevice.getAddress())) { 163 return; 164 } 165 int profileState = connected ? BluetoothProfile.STATE_CONNECTED : 166 BluetoothProfile.STATE_DISCONNECTED; 167 try { 168 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState); 169 } catch (Exception ex) { 170 Log.w(TAG, "Unhandled exception in callback", ex); 171 } 172 173 synchronized(mStateLock) { 174 if (connected) { 175 mConnState = CONN_STATE_CONNECTED; 176 } else { 177 mConnState = CONN_STATE_IDLE; 178 } 179 } 180 } 181 182 /** 183 * Callback reporting an LE scan result. 184 * @hide 185 */ 186 public void onScanResult(String address, int rssi, byte[] advData) { 187 // no op 188 } 189 190 /** 191 * A new GATT service has been discovered. 192 * The service is added to the internal list and the search 193 * continues. 194 * @hide 195 */ 196 public void onGetService(String address, int srvcType, 197 int srvcInstId, ParcelUuid srvcUuid) { 198 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); 199 if (!address.equals(mDevice.getAddress())) { 200 return; 201 } 202 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(), 203 srvcInstId, srvcType)); 204 } 205 206 /** 207 * An included service has been found durig GATT discovery. 208 * The included service is added to the respective parent. 209 * @hide 210 */ 211 public void onGetIncludedService(String address, int srvcType, 212 int srvcInstId, ParcelUuid srvcUuid, 213 int inclSrvcType, int inclSrvcInstId, 214 ParcelUuid inclSrvcUuid) { 215 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address 216 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); 217 218 if (!address.equals(mDevice.getAddress())) { 219 return; 220 } 221 BluetoothGattService service = getService(mDevice, 222 srvcUuid.getUuid(), srvcInstId, srvcType); 223 BluetoothGattService includedService = getService(mDevice, 224 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); 225 226 if (service != null && includedService != null) { 227 service.addIncludedService(includedService); 228 } 229 } 230 231 /** 232 * A new GATT characteristic has been discovered. 233 * Add the new characteristic to the relevant service and continue 234 * the remote device inspection. 235 * @hide 236 */ 237 public void onGetCharacteristic(String address, int srvcType, 238 int srvcInstId, ParcelUuid srvcUuid, 239 int charInstId, ParcelUuid charUuid, 240 int charProps) { 241 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + 242 charUuid); 243 244 if (!address.equals(mDevice.getAddress())) { 245 return; 246 } 247 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 248 srvcInstId, srvcType); 249 if (service != null) { 250 service.addCharacteristic(new BluetoothGattCharacteristic( 251 service, charUuid.getUuid(), charInstId, charProps, 0)); 252 } 253 } 254 255 /** 256 * A new GATT descriptor has been discovered. 257 * Finally, add the descriptor to the related characteristic. 258 * This should conclude the remote device update. 259 * @hide 260 */ 261 public void onGetDescriptor(String address, int srvcType, 262 int srvcInstId, ParcelUuid srvcUuid, 263 int charInstId, ParcelUuid charUuid, 264 int descrInstId, ParcelUuid descUuid) { 265 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); 266 267 if (!address.equals(mDevice.getAddress())) { 268 return; 269 } 270 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 271 srvcInstId, srvcType); 272 if (service == null) return; 273 274 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 275 charUuid.getUuid()); 276 if (characteristic == null) return; 277 278 characteristic.addDescriptor(new BluetoothGattDescriptor( 279 characteristic, descUuid.getUuid(), descrInstId, 0)); 280 } 281 282 /** 283 * Remote search has been completed. 284 * The internal object structure should now reflect the state 285 * of the remote device database. Let the application know that 286 * we are done at this point. 287 * @hide 288 */ 289 public void onSearchComplete(String address, int status) { 290 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); 291 if (!address.equals(mDevice.getAddress())) { 292 return; 293 } 294 try { 295 mCallback.onServicesDiscovered(BluetoothGatt.this, status); 296 } catch (Exception ex) { 297 Log.w(TAG, "Unhandled exception in callback", ex); 298 } 299 } 300 301 /** 302 * Remote characteristic has been read. 303 * Updates the internal value. 304 * @hide 305 */ 306 public void onCharacteristicRead(String address, int status, int srvcType, 307 int srvcInstId, ParcelUuid srvcUuid, 308 int charInstId, ParcelUuid charUuid, byte[] value) { 309 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address 310 + " UUID=" + charUuid + " Status=" + status); 311 312 if (!address.equals(mDevice.getAddress())) { 313 return; 314 } 315 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 316 || status == GATT_INSUFFICIENT_ENCRYPTION) 317 && mAuthRetry == false) { 318 try { 319 mAuthRetry = true; 320 mService.readCharacteristic(mClientIf, address, 321 srvcType, srvcInstId, srvcUuid, 322 charInstId, charUuid, AUTHENTICATION_MITM); 323 return; 324 } catch (RemoteException e) { 325 Log.e(TAG,"",e); 326 } 327 } 328 329 mAuthRetry = false; 330 331 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 332 srvcInstId, srvcType); 333 if (service == null) return; 334 335 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 336 charUuid.getUuid(), charInstId); 337 if (characteristic == null) return; 338 339 if (status == 0) characteristic.setValue(value); 340 341 try { 342 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); 343 } catch (Exception ex) { 344 Log.w(TAG, "Unhandled exception in callback", ex); 345 } 346 } 347 348 /** 349 * Characteristic has been written to the remote device. 350 * Let the app know how we did... 351 * @hide 352 */ 353 public void onCharacteristicWrite(String address, int status, int srvcType, 354 int srvcInstId, ParcelUuid srvcUuid, 355 int charInstId, ParcelUuid charUuid) { 356 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address 357 + " UUID=" + charUuid + " Status=" + status); 358 359 if (!address.equals(mDevice.getAddress())) { 360 return; 361 } 362 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 363 srvcInstId, srvcType); 364 if (service == null) return; 365 366 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 367 charUuid.getUuid(), charInstId); 368 if (characteristic == null) return; 369 370 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 371 || status == GATT_INSUFFICIENT_ENCRYPTION) 372 && mAuthRetry == false) { 373 try { 374 mAuthRetry = true; 375 mService.writeCharacteristic(mClientIf, address, 376 srvcType, srvcInstId, srvcUuid, charInstId, charUuid, 377 characteristic.getWriteType(), AUTHENTICATION_MITM, 378 characteristic.getValue()); 379 return; 380 } catch (RemoteException e) { 381 Log.e(TAG,"",e); 382 } 383 } 384 385 mAuthRetry = false; 386 387 try { 388 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status); 389 } catch (Exception ex) { 390 Log.w(TAG, "Unhandled exception in callback", ex); 391 } 392 } 393 394 /** 395 * Remote characteristic has been updated. 396 * Updates the internal value. 397 * @hide 398 */ 399 public void onNotify(String address, int srvcType, 400 int srvcInstId, ParcelUuid srvcUuid, 401 int charInstId, ParcelUuid charUuid, 402 byte[] value) { 403 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); 404 405 if (!address.equals(mDevice.getAddress())) { 406 return; 407 } 408 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 409 srvcInstId, srvcType); 410 if (service == null) return; 411 412 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 413 charUuid.getUuid(), charInstId); 414 if (characteristic == null) return; 415 416 characteristic.setValue(value); 417 418 try { 419 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic); 420 } catch (Exception ex) { 421 Log.w(TAG, "Unhandled exception in callback", ex); 422 } 423 } 424 425 /** 426 * Descriptor has been read. 427 * @hide 428 */ 429 public void onDescriptorRead(String address, int status, int srvcType, 430 int srvcInstId, ParcelUuid srvcUuid, 431 int charInstId, ParcelUuid charUuid, 432 int descrInstId, ParcelUuid descrUuid, 433 byte[] value) { 434 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); 435 436 if (!address.equals(mDevice.getAddress())) { 437 return; 438 } 439 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 440 srvcInstId, srvcType); 441 if (service == null) return; 442 443 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 444 charUuid.getUuid(), charInstId); 445 if (characteristic == null) return; 446 447 BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 448 descrUuid.getUuid(), descrInstId); 449 if (descriptor == null) return; 450 451 if (status == 0) descriptor.setValue(value); 452 453 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 454 || status == GATT_INSUFFICIENT_ENCRYPTION) 455 && mAuthRetry == false) { 456 try { 457 mAuthRetry = true; 458 mService.readDescriptor(mClientIf, address, 459 srvcType, srvcInstId, srvcUuid, charInstId, charUuid, 460 descrInstId, descrUuid, AUTHENTICATION_MITM); 461 } catch (RemoteException e) { 462 Log.e(TAG,"",e); 463 } 464 } 465 466 mAuthRetry = true; 467 468 try { 469 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); 470 } catch (Exception ex) { 471 Log.w(TAG, "Unhandled exception in callback", ex); 472 } 473 } 474 475 /** 476 * Descriptor write operation complete. 477 * @hide 478 */ 479 public void onDescriptorWrite(String address, int status, int srvcType, 480 int srvcInstId, ParcelUuid srvcUuid, 481 int charInstId, ParcelUuid charUuid, 482 int descrInstId, ParcelUuid descrUuid) { 483 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); 484 485 if (!address.equals(mDevice.getAddress())) { 486 return; 487 } 488 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 489 srvcInstId, srvcType); 490 if (service == null) return; 491 492 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 493 charUuid.getUuid(), charInstId); 494 if (characteristic == null) return; 495 496 BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 497 descrUuid.getUuid(), descrInstId); 498 if (descriptor == null) return; 499 500 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 501 || status == GATT_INSUFFICIENT_ENCRYPTION) 502 && mAuthRetry == false) { 503 try { 504 mAuthRetry = true; 505 mService.writeDescriptor(mClientIf, address, 506 srvcType, srvcInstId, srvcUuid, charInstId, charUuid, 507 descrInstId, descrUuid, characteristic.getWriteType(), 508 AUTHENTICATION_MITM, descriptor.getValue()); 509 } catch (RemoteException e) { 510 Log.e(TAG,"",e); 511 } 512 } 513 514 mAuthRetry = false; 515 516 try { 517 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); 518 } catch (Exception ex) { 519 Log.w(TAG, "Unhandled exception in callback", ex); 520 } 521 } 522 523 /** 524 * Prepared write transaction completed (or aborted) 525 * @hide 526 */ 527 public void onExecuteWrite(String address, int status) { 528 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address 529 + " status=" + status); 530 if (!address.equals(mDevice.getAddress())) { 531 return; 532 } 533 try { 534 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); 535 } catch (Exception ex) { 536 Log.w(TAG, "Unhandled exception in callback", ex); 537 } 538 } 539 540 /** 541 * Remote device RSSI has been read 542 * @hide 543 */ 544 public void onReadRemoteRssi(String address, int rssi, int status) { 545 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + 546 " rssi=" + rssi + " status=" + status); 547 if (!address.equals(mDevice.getAddress())) { 548 return; 549 } 550 try { 551 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); 552 } catch (Exception ex) { 553 Log.w(TAG, "Unhandled exception in callback", ex); 554 } 555 } 556 557 /** 558 * Advertise state change callback 559 * @hide 560 */ 561 public void onAdvertiseStateChange(int state, int status) { 562 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " 563 + state + " status=" + status); 564 } 565 }; 566 567 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { 568 mContext = context; 569 mService = iGatt; 570 mDevice = device; 571 mServices = new ArrayList<BluetoothGattService>(); 572 573 mConnState = CONN_STATE_IDLE; 574 } 575 576 /** 577 * Close this Bluetooth GATT client. 578 * 579 * Application should call this method as early as possible after it is done with 580 * this GATT client. 581 */ 582 public void close() { 583 if (DBG) Log.d(TAG, "close()"); 584 585 unregisterApp(); 586 mConnState = CONN_STATE_CLOSED; 587 } 588 589 /** 590 * Returns a service by UUID, instance and type. 591 * @hide 592 */ 593 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, 594 int instanceId, int type) { 595 for(BluetoothGattService svc : mServices) { 596 if (svc.getDevice().equals(device) && 597 svc.getType() == type && 598 svc.getInstanceId() == instanceId && 599 svc.getUuid().equals(uuid)) { 600 return svc; 601 } 602 } 603 return null; 604 } 605 606 607 /** 608 * Register an application callback to start using GATT. 609 * 610 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} 611 * is used to notify success or failure if the function returns true. 612 * 613 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 614 * 615 * @param callback GATT callback handler that will receive asynchronous callbacks. 616 * @return If true, the callback will be called to notify success or failure, 617 * false on immediate error 618 */ 619 private boolean registerApp(BluetoothGattCallback callback) { 620 if (DBG) Log.d(TAG, "registerApp()"); 621 if (mService == null) return false; 622 623 mCallback = callback; 624 UUID uuid = UUID.randomUUID(); 625 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); 626 627 try { 628 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); 629 } catch (RemoteException e) { 630 Log.e(TAG,"",e); 631 return false; 632 } 633 634 return true; 635 } 636 637 /** 638 * Unregister the current application and callbacks. 639 */ 640 private void unregisterApp() { 641 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); 642 if (mService == null || mClientIf == 0) return; 643 644 try { 645 mCallback = null; 646 mService.unregisterClient(mClientIf); 647 mClientIf = 0; 648 } catch (RemoteException e) { 649 Log.e(TAG,"",e); 650 } 651 } 652 653 /** 654 * Initiate a connection to a Bluetooth GATT capable device. 655 * 656 * <p>The connection may not be established right away, but will be 657 * completed when the remote device is available. A 658 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be 659 * invoked when the connection state changes as a result of this function. 660 * 661 * <p>The autoConnect paramter determines whether to actively connect to 662 * the remote device, or rather passively scan and finalize the connection 663 * when the remote device is in range/available. Generally, the first ever 664 * connection to a device should be direct (autoConnect set to false) and 665 * subsequent connections to known devices should be invoked with the 666 * autoConnect parameter set to true. 667 * 668 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 669 * 670 * @param device Remote device to connect to 671 * @param autoConnect Whether to directly connect to the remote device (false) 672 * or to automatically connect as soon as the remote 673 * device becomes available (true). 674 * @return true, if the connection attempt was initiated successfully 675 */ 676 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { 677 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); 678 synchronized(mStateLock) { 679 if (mConnState != CONN_STATE_IDLE) { 680 throw new IllegalStateException("Not idle"); 681 } 682 mConnState = CONN_STATE_CONNECTING; 683 } 684 if (!registerApp(callback)) { 685 synchronized(mStateLock) { 686 mConnState = CONN_STATE_IDLE; 687 } 688 Log.e(TAG, "Failed to register callback"); 689 return false; 690 } 691 692 // the connection will continue after successful callback registration 693 mAutoConnect = autoConnect; 694 return true; 695 } 696 697 /** 698 * Disconnects an established connection, or cancels a connection attempt 699 * currently in progress. 700 * 701 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 702 */ 703 public void disconnect() { 704 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); 705 if (mService == null || mClientIf == 0) return; 706 707 try { 708 mService.clientDisconnect(mClientIf, mDevice.getAddress()); 709 } catch (RemoteException e) { 710 Log.e(TAG,"",e); 711 } 712 } 713 714 /** 715 * Connect back to remote device. 716 * 717 * <p>This method is used to re-connect to a remote device after the 718 * connection has been dropped. If the device is not in range, the 719 * re-connection will be triggered once the device is back in range. 720 * 721 * @return true, if the connection attempt was initiated successfully 722 */ 723 public boolean connect() { 724 try { 725 mService.clientConnect(mClientIf, mDevice.getAddress(), 726 false); // autoConnect is inverse of "isDirect" 727 return true; 728 } catch (RemoteException e) { 729 Log.e(TAG,"",e); 730 return false; 731 } 732 } 733 734 /** 735 * Return the remote bluetooth device this GATT client targets to 736 * 737 * @return remote bluetooth device 738 */ 739 public BluetoothDevice getDevice() { 740 return mDevice; 741 } 742 743 /** 744 * Discovers services offered by a remote device as well as their 745 * characteristics and descriptors. 746 * 747 * <p>This is an asynchronous operation. Once service discovery is completed, 748 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is 749 * triggered. If the discovery was successful, the remote services can be 750 * retrieved using the {@link #getServices} function. 751 * 752 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 753 * 754 * @return true, if the remote service discovery has been started 755 */ 756 public boolean discoverServices() { 757 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); 758 if (mService == null || mClientIf == 0) return false; 759 760 mServices.clear(); 761 762 try { 763 mService.discoverServices(mClientIf, mDevice.getAddress()); 764 } catch (RemoteException e) { 765 Log.e(TAG,"",e); 766 return false; 767 } 768 769 return true; 770 } 771 772 /** 773 * Returns a list of GATT services offered by the remote device. 774 * 775 * <p>This function requires that service discovery has been completed 776 * for the given device. 777 * 778 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 779 * 780 * @return List of services on the remote device. Returns an empty list 781 * if service discovery has not yet been performed. 782 */ 783 public List<BluetoothGattService> getServices() { 784 List<BluetoothGattService> result = 785 new ArrayList<BluetoothGattService>(); 786 787 for (BluetoothGattService service : mServices) { 788 if (service.getDevice().equals(mDevice)) { 789 result.add(service); 790 } 791 } 792 793 return result; 794 } 795 796 /** 797 * Returns a {@link BluetoothGattService}, if the requested UUID is 798 * supported by the remote device. 799 * 800 * <p>This function requires that service discovery has been completed 801 * for the given device. 802 * 803 * <p>If multiple instances of the same service (as identified by UUID) 804 * exist, the first instance of the service is returned. 805 * 806 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 807 * 808 * @param uuid UUID of the requested service 809 * @return BluetoothGattService if supported, or null if the requested 810 * service is not offered by the remote device. 811 */ 812 public BluetoothGattService getService(UUID uuid) { 813 for (BluetoothGattService service : mServices) { 814 if (service.getDevice().equals(mDevice) && 815 service.getUuid().equals(uuid)) { 816 return service; 817 } 818 } 819 820 return null; 821 } 822 823 /** 824 * Reads the requested characteristic from the associated remote device. 825 * 826 * <p>This is an asynchronous operation. The result of the read operation 827 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} 828 * callback. 829 * 830 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 831 * 832 * @param characteristic Characteristic to read from the remote device 833 * @return true, if the read operation was initiated successfully 834 */ 835 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 836 if ((characteristic.getProperties() & 837 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; 838 839 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); 840 if (mService == null || mClientIf == 0) return false; 841 842 BluetoothGattService service = characteristic.getService(); 843 if (service == null) return false; 844 845 BluetoothDevice device = service.getDevice(); 846 if (device == null) return false; 847 848 try { 849 mService.readCharacteristic(mClientIf, device.getAddress(), 850 service.getType(), service.getInstanceId(), 851 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 852 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); 853 } catch (RemoteException e) { 854 Log.e(TAG,"",e); 855 return false; 856 } 857 858 return true; 859 } 860 861 /** 862 * Writes a given characteristic and its values to the associated remote device. 863 * 864 * <p>Once the write operation has been completed, the 865 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, 866 * reporting the result of the operation. 867 * 868 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 869 * 870 * @param characteristic Characteristic to write on the remote device 871 * @return true, if the write operation was initiated successfully 872 */ 873 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 874 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 875 && (characteristic.getProperties() & 876 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; 877 878 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); 879 if (mService == null || mClientIf == 0) return false; 880 881 BluetoothGattService service = characteristic.getService(); 882 if (service == null) return false; 883 884 BluetoothDevice device = service.getDevice(); 885 if (device == null) return false; 886 887 try { 888 mService.writeCharacteristic(mClientIf, device.getAddress(), 889 service.getType(), service.getInstanceId(), 890 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 891 new ParcelUuid(characteristic.getUuid()), 892 characteristic.getWriteType(), AUTHENTICATION_NONE, 893 characteristic.getValue()); 894 } catch (RemoteException e) { 895 Log.e(TAG,"",e); 896 return false; 897 } 898 899 return true; 900 } 901 902 /** 903 * Reads the value for a given descriptor from the associated remote device. 904 * 905 * <p>Once the read operation has been completed, the 906 * {@link BluetoothGattCallback#onDescriptorRead} callback is 907 * triggered, signaling the result of the operation. 908 * 909 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 910 * 911 * @param descriptor Descriptor value to read from the remote device 912 * @return true, if the read operation was initiated successfully 913 */ 914 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 915 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); 916 if (mService == null || mClientIf == 0) return false; 917 918 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 919 if (characteristic == null) return false; 920 921 BluetoothGattService service = characteristic.getService(); 922 if (service == null) return false; 923 924 BluetoothDevice device = service.getDevice(); 925 if (device == null) return false; 926 927 try { 928 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), 929 service.getInstanceId(), new ParcelUuid(service.getUuid()), 930 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), 931 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), 932 AUTHENTICATION_NONE); 933 } catch (RemoteException e) { 934 Log.e(TAG,"",e); 935 return false; 936 } 937 938 return true; 939 } 940 941 /** 942 * Write the value of a given descriptor to the associated remote device. 943 * 944 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is 945 * triggered to report the result of the write operation. 946 * 947 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 948 * 949 * @param descriptor Descriptor to write to the associated remote device 950 * @return true, if the write operation was initiated successfully 951 */ 952 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 953 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); 954 if (mService == null || mClientIf == 0) return false; 955 956 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 957 if (characteristic == null) return false; 958 959 BluetoothGattService service = characteristic.getService(); 960 if (service == null) return false; 961 962 BluetoothDevice device = service.getDevice(); 963 if (device == null) return false; 964 965 try { 966 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), 967 service.getInstanceId(), new ParcelUuid(service.getUuid()), 968 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), 969 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), 970 characteristic.getWriteType(), AUTHENTICATION_NONE, 971 descriptor.getValue()); 972 } catch (RemoteException e) { 973 Log.e(TAG,"",e); 974 return false; 975 } 976 977 return true; 978 } 979 980 /** 981 * Initiates a reliable write transaction for a given remote device. 982 * 983 * <p>Once a reliable write transaction has been initiated, all calls 984 * to {@link #writeCharacteristic} are sent to the remote device for 985 * verification and queued up for atomic execution. The application will 986 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback 987 * in response to every {@link #writeCharacteristic} call and is responsible 988 * for verifying if the value has been transmitted accurately. 989 * 990 * <p>After all characteristics have been queued up and verified, 991 * {@link #executeReliableWrite} will execute all writes. If a characteristic 992 * was not written correctly, calling {@link #abortReliableWrite} will 993 * cancel the current transaction without commiting any values on the 994 * remote device. 995 * 996 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 997 * 998 * @return true, if the reliable write transaction has been initiated 999 */ 1000 public boolean beginReliableWrite() { 1001 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); 1002 if (mService == null || mClientIf == 0) return false; 1003 1004 try { 1005 mService.beginReliableWrite(mClientIf, mDevice.getAddress()); 1006 } catch (RemoteException e) { 1007 Log.e(TAG,"",e); 1008 return false; 1009 } 1010 1011 return true; 1012 } 1013 1014 /** 1015 * Executes a reliable write transaction for a given remote device. 1016 * 1017 * <p>This function will commit all queued up characteristic write 1018 * operations for a given remote device. 1019 * 1020 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is 1021 * invoked to indicate whether the transaction has been executed correctly. 1022 * 1023 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1024 * 1025 * @return true, if the request to execute the transaction has been sent 1026 */ 1027 public boolean executeReliableWrite() { 1028 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); 1029 if (mService == null || mClientIf == 0) return false; 1030 1031 try { 1032 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); 1033 } catch (RemoteException e) { 1034 Log.e(TAG,"",e); 1035 return false; 1036 } 1037 1038 return true; 1039 } 1040 1041 /** 1042 * Cancels a reliable write transaction for a given device. 1043 * 1044 * <p>Calling this function will discard all queued characteristic write 1045 * operations for a given remote device. 1046 * 1047 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1048 */ 1049 public void abortReliableWrite() { 1050 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); 1051 if (mService == null || mClientIf == 0) return; 1052 1053 try { 1054 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); 1055 } catch (RemoteException e) { 1056 Log.e(TAG,"",e); 1057 } 1058 } 1059 1060 /** 1061 * @deprecated Use {@link #abortReliableWrite()} 1062 */ 1063 public void abortReliableWrite(BluetoothDevice mDevice) { 1064 abortReliableWrite(); 1065 } 1066 1067 /** 1068 * Enable or disable notifications/indications for a given characteristic. 1069 * 1070 * <p>Once notifications are enabled for a characteristic, a 1071 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be 1072 * triggered if the remote device indicates that the given characteristic 1073 * has changed. 1074 * 1075 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1076 * 1077 * @param characteristic The characteristic for which to enable notifications 1078 * @param enable Set to true to enable notifications/indications 1079 * @return true, if the requested notification status was set successfully 1080 */ 1081 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, 1082 boolean enable) { 1083 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() 1084 + " enable: " + enable); 1085 if (mService == null || mClientIf == 0) return false; 1086 1087 BluetoothGattService service = characteristic.getService(); 1088 if (service == null) return false; 1089 1090 BluetoothDevice device = service.getDevice(); 1091 if (device == null) return false; 1092 1093 try { 1094 mService.registerForNotification(mClientIf, device.getAddress(), 1095 service.getType(), service.getInstanceId(), 1096 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 1097 new ParcelUuid(characteristic.getUuid()), 1098 enable); 1099 } catch (RemoteException e) { 1100 Log.e(TAG,"",e); 1101 return false; 1102 } 1103 1104 return true; 1105 } 1106 1107 /** 1108 * Clears the internal cache and forces a refresh of the services from the 1109 * remote device. 1110 * @hide 1111 */ 1112 public boolean refresh() { 1113 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); 1114 if (mService == null || mClientIf == 0) return false; 1115 1116 try { 1117 mService.refreshDevice(mClientIf, mDevice.getAddress()); 1118 } catch (RemoteException e) { 1119 Log.e(TAG,"",e); 1120 return false; 1121 } 1122 1123 return true; 1124 } 1125 1126 /** 1127 * Read the RSSI for a connected remote device. 1128 * 1129 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be 1130 * invoked when the RSSI value has been read. 1131 * 1132 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1133 * 1134 * @return true, if the RSSI value has been requested successfully 1135 */ 1136 public boolean readRemoteRssi() { 1137 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); 1138 if (mService == null || mClientIf == 0) return false; 1139 1140 try { 1141 mService.readRemoteRssi(mClientIf, mDevice.getAddress()); 1142 } catch (RemoteException e) { 1143 Log.e(TAG,"",e); 1144 return false; 1145 } 1146 1147 return true; 1148 } 1149 1150 /** 1151 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1152 * with {@link BluetoothProfile#GATT} as argument 1153 * 1154 * @throws UnsupportedOperationException 1155 */ 1156 @Override 1157 public int getConnectionState(BluetoothDevice device) { 1158 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 1159 } 1160 1161 /** 1162 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1163 * with {@link BluetoothProfile#GATT} as argument 1164 * 1165 * @throws UnsupportedOperationException 1166 */ 1167 @Override 1168 public List<BluetoothDevice> getConnectedDevices() { 1169 throw new UnsupportedOperationException 1170 ("Use BluetoothManager#getConnectedDevices instead."); 1171 } 1172 1173 /** 1174 * Not supported - please use 1175 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 1176 * with {@link BluetoothProfile#GATT} as first argument 1177 * 1178 * @throws UnsupportedOperationException 1179 */ 1180 @Override 1181 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1182 throw new UnsupportedOperationException 1183 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 1184 } 1185 } 1186