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: " + 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 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(), 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: " + 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: " + 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: " + 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: " + 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 ParcelUuid descrUuid, byte[] value) { 433 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); 434 435 if (!address.equals(mDevice.getAddress())) { 436 return; 437 } 438 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 439 srvcInstId, srvcType); 440 if (service == null) return; 441 442 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 443 charUuid.getUuid(), charInstId); 444 if (characteristic == null) return; 445 446 BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 447 descrUuid.getUuid()); 448 if (descriptor == null) return; 449 450 if (status == 0) descriptor.setValue(value); 451 452 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 453 || status == GATT_INSUFFICIENT_ENCRYPTION) 454 && mAuthRetry == false) { 455 try { 456 mAuthRetry = true; 457 mService.readDescriptor(mClientIf, address, 458 srvcType, srvcInstId, srvcUuid, charInstId, charUuid, 459 descrUuid, AUTHENTICATION_MITM); 460 } catch (RemoteException e) { 461 Log.e(TAG,"",e); 462 } 463 } 464 465 mAuthRetry = true; 466 467 try { 468 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status); 469 } catch (Exception ex) { 470 Log.w(TAG, "Unhandled exception: " + ex); 471 } 472 } 473 474 /** 475 * Descriptor write operation complete. 476 * @hide 477 */ 478 public void onDescriptorWrite(String address, int status, int srvcType, 479 int srvcInstId, ParcelUuid srvcUuid, 480 int charInstId, ParcelUuid charUuid, 481 ParcelUuid descrUuid) { 482 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); 483 484 if (!address.equals(mDevice.getAddress())) { 485 return; 486 } 487 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), 488 srvcInstId, srvcType); 489 if (service == null) return; 490 491 BluetoothGattCharacteristic characteristic = service.getCharacteristic( 492 charUuid.getUuid(), charInstId); 493 if (characteristic == null) return; 494 495 BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 496 descrUuid.getUuid()); 497 if (descriptor == null) return; 498 499 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 500 || status == GATT_INSUFFICIENT_ENCRYPTION) 501 && mAuthRetry == false) { 502 try { 503 mAuthRetry = true; 504 mService.writeDescriptor(mClientIf, address, 505 srvcType, srvcInstId, srvcUuid, charInstId, charUuid, 506 descrUuid, characteristic.getWriteType(), 507 AUTHENTICATION_MITM, descriptor.getValue()); 508 } catch (RemoteException e) { 509 Log.e(TAG,"",e); 510 } 511 } 512 513 mAuthRetry = false; 514 515 try { 516 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); 517 } catch (Exception ex) { 518 Log.w(TAG, "Unhandled exception: " + ex); 519 } 520 } 521 522 /** 523 * Prepared write transaction completed (or aborted) 524 * @hide 525 */ 526 public void onExecuteWrite(String address, int status) { 527 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address 528 + " status=" + status); 529 if (!address.equals(mDevice.getAddress())) { 530 return; 531 } 532 try { 533 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); 534 } catch (Exception ex) { 535 Log.w(TAG, "Unhandled exception: " + ex); 536 } 537 } 538 539 /** 540 * Remote device RSSI has been read 541 * @hide 542 */ 543 public void onReadRemoteRssi(String address, int rssi, int status) { 544 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + 545 " rssi=" + rssi + " status=" + status); 546 if (!address.equals(mDevice.getAddress())) { 547 return; 548 } 549 try { 550 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); 551 } catch (Exception ex) { 552 Log.w(TAG, "Unhandled exception: " + ex); 553 } 554 } 555 }; 556 BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device)557 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { 558 mContext = context; 559 mService = iGatt; 560 mDevice = device; 561 mServices = new ArrayList<BluetoothGattService>(); 562 563 mConnState = CONN_STATE_IDLE; 564 } 565 566 /** 567 * Close this Bluetooth GATT client. 568 * 569 * Application should call this method as early as possible after it is done with 570 * this GATT client. 571 */ close()572 public void close() { 573 if (DBG) Log.d(TAG, "close()"); 574 575 unregisterApp(); 576 mConnState = CONN_STATE_CLOSED; 577 } 578 579 /** 580 * Returns a service by UUID, instance and type. 581 * @hide 582 */ getService(BluetoothDevice device, UUID uuid, int instanceId, int type)583 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, 584 int instanceId, int type) { 585 for(BluetoothGattService svc : mServices) { 586 if (svc.getDevice().equals(device) && 587 svc.getType() == type && 588 svc.getInstanceId() == instanceId && 589 svc.getUuid().equals(uuid)) { 590 return svc; 591 } 592 } 593 return null; 594 } 595 596 597 /** 598 * Register an application callback to start using GATT. 599 * 600 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} 601 * is used to notify success or failure if the function returns true. 602 * 603 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 604 * 605 * @param callback GATT callback handler that will receive asynchronous callbacks. 606 * @return If true, the callback will be called to notify success or failure, 607 * false on immediate error 608 */ registerApp(BluetoothGattCallback callback)609 private boolean registerApp(BluetoothGattCallback callback) { 610 if (DBG) Log.d(TAG, "registerApp()"); 611 if (mService == null) return false; 612 613 mCallback = callback; 614 UUID uuid = UUID.randomUUID(); 615 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); 616 617 try { 618 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); 619 } catch (RemoteException e) { 620 Log.e(TAG,"",e); 621 return false; 622 } 623 624 return true; 625 } 626 627 /** 628 * Unregister the current application and callbacks. 629 */ unregisterApp()630 private void unregisterApp() { 631 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); 632 if (mService == null || mClientIf == 0) return; 633 634 try { 635 mCallback = null; 636 mService.unregisterClient(mClientIf); 637 mClientIf = 0; 638 } catch (RemoteException e) { 639 Log.e(TAG,"",e); 640 } 641 } 642 643 /** 644 * Initiate a connection to a Bluetooth GATT capable device. 645 * 646 * <p>The connection may not be established right away, but will be 647 * completed when the remote device is available. A 648 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be 649 * invoked when the connection state changes as a result of this function. 650 * 651 * <p>The autoConnect paramter determines whether to actively connect to 652 * the remote device, or rather passively scan and finalize the connection 653 * when the remote device is in range/available. Generally, the first ever 654 * connection to a device should be direct (autoConnect set to false) and 655 * subsequent connections to known devices should be invoked with the 656 * autoConnect parameter set to true. 657 * 658 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 659 * 660 * @param device Remote device to connect to 661 * @param autoConnect Whether to directly connect to the remote device (false) 662 * or to automatically connect as soon as the remote 663 * device becomes available (true). 664 * @return true, if the connection attempt was initiated successfully 665 */ connect(Boolean autoConnect, BluetoothGattCallback callback)666 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { 667 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); 668 synchronized(mStateLock) { 669 if (mConnState != CONN_STATE_IDLE) { 670 throw new IllegalStateException("Not idle"); 671 } 672 mConnState = CONN_STATE_CONNECTING; 673 } 674 if (!registerApp(callback)) { 675 synchronized(mStateLock) { 676 mConnState = CONN_STATE_IDLE; 677 } 678 Log.e(TAG, "Failed to register callback"); 679 return false; 680 } 681 682 // the connection will continue after successful callback registration 683 mAutoConnect = autoConnect; 684 return true; 685 } 686 687 /** 688 * Disconnects an established connection, or cancels a connection attempt 689 * currently in progress. 690 * 691 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 692 */ disconnect()693 public void disconnect() { 694 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); 695 if (mService == null || mClientIf == 0) return; 696 697 try { 698 mService.clientDisconnect(mClientIf, mDevice.getAddress()); 699 } catch (RemoteException e) { 700 Log.e(TAG,"",e); 701 } 702 } 703 704 /** 705 * Connect back to remote device. 706 * 707 * <p>This method is used to re-connect to a remote device after the 708 * connection has been dropped. If the device is not in range, the 709 * re-connection will be triggered once the device is back in range. 710 * 711 * @return true, if the connection attempt was initiated successfully 712 */ connect()713 public boolean connect() { 714 try { 715 mService.clientConnect(mClientIf, mDevice.getAddress(), 716 false); // autoConnect is inverse of "isDirect" 717 return true; 718 } catch (RemoteException e) { 719 Log.e(TAG,"",e); 720 return false; 721 } 722 } 723 724 /** 725 * Return the remote bluetooth device this GATT client targets to 726 * 727 * @return remote bluetooth device 728 */ getDevice()729 public BluetoothDevice getDevice() { 730 return mDevice; 731 } 732 733 /** 734 * Discovers services offered by a remote device as well as their 735 * characteristics and descriptors. 736 * 737 * <p>This is an asynchronous operation. Once service discovery is completed, 738 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is 739 * triggered. If the discovery was successful, the remote services can be 740 * retrieved using the {@link #getServices} function. 741 * 742 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 743 * 744 * @return true, if the remote service discovery has been started 745 */ discoverServices()746 public boolean discoverServices() { 747 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); 748 if (mService == null || mClientIf == 0) return false; 749 750 mServices.clear(); 751 752 try { 753 mService.discoverServices(mClientIf, mDevice.getAddress()); 754 } catch (RemoteException e) { 755 Log.e(TAG,"",e); 756 return false; 757 } 758 759 return true; 760 } 761 762 /** 763 * Returns a list of GATT services offered by the remote device. 764 * 765 * <p>This function requires that service discovery has been completed 766 * for the given device. 767 * 768 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 769 * 770 * @return List of services on the remote device. Returns an empty list 771 * if service discovery has not yet been performed. 772 */ getServices()773 public List<BluetoothGattService> getServices() { 774 List<BluetoothGattService> result = 775 new ArrayList<BluetoothGattService>(); 776 777 for (BluetoothGattService service : mServices) { 778 if (service.getDevice().equals(mDevice)) { 779 result.add(service); 780 } 781 } 782 783 return result; 784 } 785 786 /** 787 * Returns a {@link BluetoothGattService}, if the requested UUID is 788 * supported by the remote device. 789 * 790 * <p>This function requires that service discovery has been completed 791 * for the given device. 792 * 793 * <p>If multiple instances of the same service (as identified by UUID) 794 * exist, the first instance of the service is returned. 795 * 796 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 797 * 798 * @param uuid UUID of the requested service 799 * @return BluetoothGattService if supported, or null if the requested 800 * service is not offered by the remote device. 801 */ getService(UUID uuid)802 public BluetoothGattService getService(UUID uuid) { 803 for (BluetoothGattService service : mServices) { 804 if (service.getDevice().equals(mDevice) && 805 service.getUuid().equals(uuid)) { 806 return service; 807 } 808 } 809 810 return null; 811 } 812 813 /** 814 * Reads the requested characteristic from the associated remote device. 815 * 816 * <p>This is an asynchronous operation. The result of the read operation 817 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} 818 * callback. 819 * 820 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 821 * 822 * @param characteristic Characteristic to read from the remote device 823 * @return true, if the read operation was initiated successfully 824 */ readCharacteristic(BluetoothGattCharacteristic characteristic)825 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 826 if ((characteristic.getProperties() & 827 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; 828 829 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); 830 if (mService == null || mClientIf == 0) return false; 831 832 BluetoothGattService service = characteristic.getService(); 833 if (service == null) return false; 834 835 BluetoothDevice device = service.getDevice(); 836 if (device == null) return false; 837 838 try { 839 mService.readCharacteristic(mClientIf, device.getAddress(), 840 service.getType(), service.getInstanceId(), 841 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 842 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); 843 } catch (RemoteException e) { 844 Log.e(TAG,"",e); 845 return false; 846 } 847 848 return true; 849 } 850 851 /** 852 * Writes a given characteristic and its values to the associated remote device. 853 * 854 * <p>Once the write operation has been completed, the 855 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, 856 * reporting the result of the operation. 857 * 858 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 859 * 860 * @param characteristic Characteristic to write on the remote device 861 * @return true, if the write operation was initiated successfully 862 */ writeCharacteristic(BluetoothGattCharacteristic characteristic)863 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 864 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 865 && (characteristic.getProperties() & 866 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; 867 868 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); 869 if (mService == null || mClientIf == 0) return false; 870 871 BluetoothGattService service = characteristic.getService(); 872 if (service == null) return false; 873 874 BluetoothDevice device = service.getDevice(); 875 if (device == null) return false; 876 877 try { 878 mService.writeCharacteristic(mClientIf, device.getAddress(), 879 service.getType(), service.getInstanceId(), 880 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 881 new ParcelUuid(characteristic.getUuid()), 882 characteristic.getWriteType(), AUTHENTICATION_NONE, 883 characteristic.getValue()); 884 } catch (RemoteException e) { 885 Log.e(TAG,"",e); 886 return false; 887 } 888 889 return true; 890 } 891 892 /** 893 * Reads the value for a given descriptor from the associated remote device. 894 * 895 * <p>Once the read operation has been completed, the 896 * {@link BluetoothGattCallback#onDescriptorRead} callback is 897 * triggered, signaling the result of the operation. 898 * 899 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 900 * 901 * @param descriptor Descriptor value to read from the remote device 902 * @return true, if the read operation was initiated successfully 903 */ readDescriptor(BluetoothGattDescriptor descriptor)904 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 905 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); 906 if (mService == null || mClientIf == 0) return false; 907 908 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 909 if (characteristic == null) return false; 910 911 BluetoothGattService service = characteristic.getService(); 912 if (service == null) return false; 913 914 BluetoothDevice device = service.getDevice(); 915 if (device == null) return false; 916 917 try { 918 mService.readDescriptor(mClientIf, device.getAddress(), 919 service.getType(), service.getInstanceId(), 920 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 921 new ParcelUuid(characteristic.getUuid()), 922 new ParcelUuid(descriptor.getUuid()), AUTHENTICATION_NONE); 923 } catch (RemoteException e) { 924 Log.e(TAG,"",e); 925 return false; 926 } 927 928 return true; 929 } 930 931 /** 932 * Write the value of a given descriptor to the associated remote device. 933 * 934 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is 935 * triggered to report the result of the write operation. 936 * 937 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 938 * 939 * @param descriptor Descriptor to write to the associated remote device 940 * @return true, if the write operation was initiated successfully 941 */ writeDescriptor(BluetoothGattDescriptor descriptor)942 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 943 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); 944 if (mService == null || mClientIf == 0) return false; 945 946 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 947 if (characteristic == null) return false; 948 949 BluetoothGattService service = characteristic.getService(); 950 if (service == null) return false; 951 952 BluetoothDevice device = service.getDevice(); 953 if (device == null) return false; 954 955 try { 956 mService.writeDescriptor(mClientIf, device.getAddress(), 957 service.getType(), service.getInstanceId(), 958 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 959 new ParcelUuid(characteristic.getUuid()), 960 new ParcelUuid(descriptor.getUuid()), 961 characteristic.getWriteType(), AUTHENTICATION_NONE, 962 descriptor.getValue()); 963 } catch (RemoteException e) { 964 Log.e(TAG,"",e); 965 return false; 966 } 967 968 return true; 969 } 970 971 /** 972 * Initiates a reliable write transaction for a given remote device. 973 * 974 * <p>Once a reliable write transaction has been initiated, all calls 975 * to {@link #writeCharacteristic} are sent to the remote device for 976 * verification and queued up for atomic execution. The application will 977 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback 978 * in response to every {@link #writeCharacteristic} call and is responsible 979 * for verifying if the value has been transmitted accurately. 980 * 981 * <p>After all characteristics have been queued up and verified, 982 * {@link #executeReliableWrite} will execute all writes. If a characteristic 983 * was not written correctly, calling {@link #abortReliableWrite} will 984 * cancel the current transaction without commiting any values on the 985 * remote device. 986 * 987 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 988 * 989 * @return true, if the reliable write transaction has been initiated 990 */ beginReliableWrite()991 public boolean beginReliableWrite() { 992 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); 993 if (mService == null || mClientIf == 0) return false; 994 995 try { 996 mService.beginReliableWrite(mClientIf, mDevice.getAddress()); 997 } catch (RemoteException e) { 998 Log.e(TAG,"",e); 999 return false; 1000 } 1001 1002 return true; 1003 } 1004 1005 /** 1006 * Executes a reliable write transaction for a given remote device. 1007 * 1008 * <p>This function will commit all queued up characteristic write 1009 * operations for a given remote device. 1010 * 1011 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is 1012 * invoked to indicate whether the transaction has been executed correctly. 1013 * 1014 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1015 * 1016 * @return true, if the request to execute the transaction has been sent 1017 */ executeReliableWrite()1018 public boolean executeReliableWrite() { 1019 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); 1020 if (mService == null || mClientIf == 0) return false; 1021 1022 try { 1023 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); 1024 } catch (RemoteException e) { 1025 Log.e(TAG,"",e); 1026 return false; 1027 } 1028 1029 return true; 1030 } 1031 1032 /** 1033 * Cancels a reliable write transaction for a given device. 1034 * 1035 * <p>Calling this function will discard all queued characteristic write 1036 * operations for a given remote device. 1037 * 1038 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1039 */ abortReliableWrite(BluetoothDevice mDevice)1040 public void abortReliableWrite(BluetoothDevice mDevice) { 1041 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); 1042 if (mService == null || mClientIf == 0) return; 1043 1044 try { 1045 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); 1046 } catch (RemoteException e) { 1047 Log.e(TAG,"",e); 1048 } 1049 } 1050 1051 /** 1052 * Enable or disable notifications/indications for a given characteristic. 1053 * 1054 * <p>Once notifications are enabled for a characteristic, a 1055 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be 1056 * triggered if the remote device indicates that the given characteristic 1057 * has changed. 1058 * 1059 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1060 * 1061 * @param characteristic The characteristic for which to enable notifications 1062 * @param enable Set to true to enable notifications/indications 1063 * @return true, if the requested notification status was set successfully 1064 */ setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable)1065 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, 1066 boolean enable) { 1067 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() 1068 + " enable: " + enable); 1069 if (mService == null || mClientIf == 0) return false; 1070 1071 BluetoothGattService service = characteristic.getService(); 1072 if (service == null) return false; 1073 1074 BluetoothDevice device = service.getDevice(); 1075 if (device == null) return false; 1076 1077 try { 1078 mService.registerForNotification(mClientIf, device.getAddress(), 1079 service.getType(), service.getInstanceId(), 1080 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 1081 new ParcelUuid(characteristic.getUuid()), 1082 enable); 1083 } catch (RemoteException e) { 1084 Log.e(TAG,"",e); 1085 return false; 1086 } 1087 1088 return true; 1089 } 1090 1091 /** 1092 * Clears the internal cache and forces a refresh of the services from the 1093 * remote device. 1094 * @hide 1095 */ refresh()1096 public boolean refresh() { 1097 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); 1098 if (mService == null || mClientIf == 0) return false; 1099 1100 try { 1101 mService.refreshDevice(mClientIf, mDevice.getAddress()); 1102 } catch (RemoteException e) { 1103 Log.e(TAG,"",e); 1104 return false; 1105 } 1106 1107 return true; 1108 } 1109 1110 /** 1111 * Read the RSSI for a connected remote device. 1112 * 1113 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be 1114 * invoked when the RSSI value has been read. 1115 * 1116 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1117 * 1118 * @return true, if the RSSI value has been requested successfully 1119 */ readRemoteRssi()1120 public boolean readRemoteRssi() { 1121 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); 1122 if (mService == null || mClientIf == 0) return false; 1123 1124 try { 1125 mService.readRemoteRssi(mClientIf, mDevice.getAddress()); 1126 } catch (RemoteException e) { 1127 Log.e(TAG,"",e); 1128 return false; 1129 } 1130 1131 return true; 1132 } 1133 1134 /** 1135 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1136 * with {@link BluetoothProfile#GATT} as argument 1137 * 1138 * @throws UnsupportedOperationException 1139 */ 1140 @Override getConnectionState(BluetoothDevice device)1141 public int getConnectionState(BluetoothDevice device) { 1142 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 1143 } 1144 1145 /** 1146 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1147 * with {@link BluetoothProfile#GATT} as argument 1148 * 1149 * @throws UnsupportedOperationException 1150 */ 1151 @Override getConnectedDevices()1152 public List<BluetoothDevice> getConnectedDevices() { 1153 throw new UnsupportedOperationException 1154 ("Use BluetoothManager#getConnectedDevices instead."); 1155 } 1156 1157 /** 1158 * Not supported - please use 1159 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 1160 * with {@link BluetoothProfile#GATT} as first argument 1161 * 1162 * @throws UnsupportedOperationException 1163 */ 1164 @Override getDevicesMatchingConnectionStates(int[] states)1165 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1166 throw new UnsupportedOperationException 1167 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 1168 } 1169 } 1170