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 17package android.bluetooth; 18 19import android.bluetooth.BluetoothDevice; 20import android.bluetooth.BluetoothProfile; 21import android.bluetooth.BluetoothProfile.ServiceListener; 22import android.bluetooth.IBluetoothManager; 23import android.bluetooth.IBluetoothStateChangeCallback; 24 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.ServiceConnection; 29import android.os.IBinder; 30import android.os.ParcelUuid; 31import android.os.RemoteException; 32import android.os.ServiceManager; 33import android.util.Log; 34 35import java.util.ArrayList; 36import java.util.List; 37import 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 */ 50public 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 * Listen command status callback 559 * @hide 560 */ 561 public void onListen(int status) { 562 if (DBG) Log.d(TAG, "onListen() - status=" + status); 563 } 564 }; 565 566 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { 567 mContext = context; 568 mService = iGatt; 569 mDevice = device; 570 mServices = new ArrayList<BluetoothGattService>(); 571 572 mConnState = CONN_STATE_IDLE; 573 } 574 575 /** 576 * Close this Bluetooth GATT client. 577 * 578 * Application should call this method as early as possible after it is done with 579 * this GATT client. 580 */ 581 public void close() { 582 if (DBG) Log.d(TAG, "close()"); 583 584 unregisterApp(); 585 mConnState = CONN_STATE_CLOSED; 586 } 587 588 /** 589 * Returns a service by UUID, instance and type. 590 * @hide 591 */ 592 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, 593 int instanceId, int type) { 594 for(BluetoothGattService svc : mServices) { 595 if (svc.getDevice().equals(device) && 596 svc.getType() == type && 597 svc.getInstanceId() == instanceId && 598 svc.getUuid().equals(uuid)) { 599 return svc; 600 } 601 } 602 return null; 603 } 604 605 606 /** 607 * Register an application callback to start using GATT. 608 * 609 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} 610 * is used to notify success or failure if the function returns true. 611 * 612 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 613 * 614 * @param callback GATT callback handler that will receive asynchronous callbacks. 615 * @return If true, the callback will be called to notify success or failure, 616 * false on immediate error 617 */ 618 private boolean registerApp(BluetoothGattCallback callback) { 619 if (DBG) Log.d(TAG, "registerApp()"); 620 if (mService == null) return false; 621 622 mCallback = callback; 623 UUID uuid = UUID.randomUUID(); 624 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); 625 626 try { 627 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); 628 } catch (RemoteException e) { 629 Log.e(TAG,"",e); 630 return false; 631 } 632 633 return true; 634 } 635 636 /** 637 * Unregister the current application and callbacks. 638 */ 639 private void unregisterApp() { 640 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); 641 if (mService == null || mClientIf == 0) return; 642 643 try { 644 mCallback = null; 645 mService.unregisterClient(mClientIf); 646 mClientIf = 0; 647 } catch (RemoteException e) { 648 Log.e(TAG,"",e); 649 } 650 } 651 652 /** 653 * Initiate a connection to a Bluetooth GATT capable device. 654 * 655 * <p>The connection may not be established right away, but will be 656 * completed when the remote device is available. A 657 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be 658 * invoked when the connection state changes as a result of this function. 659 * 660 * <p>The autoConnect paramter determines whether to actively connect to 661 * the remote device, or rather passively scan and finalize the connection 662 * when the remote device is in range/available. Generally, the first ever 663 * connection to a device should be direct (autoConnect set to false) and 664 * subsequent connections to known devices should be invoked with the 665 * autoConnect parameter set to true. 666 * 667 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 668 * 669 * @param device Remote device to connect to 670 * @param autoConnect Whether to directly connect to the remote device (false) 671 * or to automatically connect as soon as the remote 672 * device becomes available (true). 673 * @return true, if the connection attempt was initiated successfully 674 */ 675 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { 676 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); 677 synchronized(mStateLock) { 678 if (mConnState != CONN_STATE_IDLE) { 679 throw new IllegalStateException("Not idle"); 680 } 681 mConnState = CONN_STATE_CONNECTING; 682 } 683 if (!registerApp(callback)) { 684 synchronized(mStateLock) { 685 mConnState = CONN_STATE_IDLE; 686 } 687 Log.e(TAG, "Failed to register callback"); 688 return false; 689 } 690 691 // the connection will continue after successful callback registration 692 mAutoConnect = autoConnect; 693 return true; 694 } 695 696 /** 697 * Starts or stops sending of advertisement packages to listen for connection 698 * requests from a central devices. 699 * 700 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 701 * 702 * @param start Start or stop advertising 703 */ 704 /*package*/ void listen(boolean start) { 705 if (mContext == null || !mContext.getResources(). 706 getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { 707 throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); 708 } 709 if (DBG) Log.d(TAG, "listen() - start: " + start); 710 if (mService == null || mClientIf == 0) return; 711 712 try { 713 mService.clientListen(mClientIf, start); 714 } catch (RemoteException e) { 715 Log.e(TAG,"",e); 716 } 717 } 718 719 /** 720 * Sets the advertising data contained in the adv. response packet. 721 * 722 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 723 * 724 * @param advData true to set adv. data, false to set scan response 725 * @param includeName Inlucde the name in the adv. response 726 * @param includeTxPower Include TX power value 727 * @param minInterval Minimum desired scan interval (optional) 728 * @param maxInterval Maximum desired scan interval (optional) 729 * @param appearance The appearance flags for the device (optional) 730 * @param manufacturerData Manufacturer specific data including company ID (optional) 731 */ 732 /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, 733 Integer minInterval, Integer maxInterval, 734 Integer appearance, Byte[] manufacturerData) { 735 if (mContext == null || !mContext.getResources(). 736 getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { 737 throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); 738 } 739 if (DBG) Log.d(TAG, "setAdvData()"); 740 if (mService == null || mClientIf == 0) return; 741 742 byte[] data = new byte[0]; 743 if (manufacturerData != null) { 744 data = new byte[manufacturerData.length]; 745 for(int i = 0; i != manufacturerData.length; ++i) { 746 data[i] = manufacturerData[i]; 747 } 748 } 749 750 try { 751 mService.setAdvData(mClientIf, !advData, 752 includeName, includeTxPower, 753 minInterval != null ? minInterval : 0, 754 maxInterval != null ? maxInterval : 0, 755 appearance != null ? appearance : 0, data); 756 } catch (RemoteException e) { 757 Log.e(TAG,"",e); 758 } 759 } 760 761 /** 762 * Disconnects an established connection, or cancels a connection attempt 763 * currently in progress. 764 * 765 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 766 */ 767 public void disconnect() { 768 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); 769 if (mService == null || mClientIf == 0) return; 770 771 try { 772 mService.clientDisconnect(mClientIf, mDevice.getAddress()); 773 } catch (RemoteException e) { 774 Log.e(TAG,"",e); 775 } 776 } 777 778 /** 779 * Connect back to remote device. 780 * 781 * <p>This method is used to re-connect to a remote device after the 782 * connection has been dropped. If the device is not in range, the 783 * re-connection will be triggered once the device is back in range. 784 * 785 * @return true, if the connection attempt was initiated successfully 786 */ 787 public boolean connect() { 788 try { 789 mService.clientConnect(mClientIf, mDevice.getAddress(), 790 false); // autoConnect is inverse of "isDirect" 791 return true; 792 } catch (RemoteException e) { 793 Log.e(TAG,"",e); 794 return false; 795 } 796 } 797 798 /** 799 * Return the remote bluetooth device this GATT client targets to 800 * 801 * @return remote bluetooth device 802 */ 803 public BluetoothDevice getDevice() { 804 return mDevice; 805 } 806 807 /** 808 * Discovers services offered by a remote device as well as their 809 * characteristics and descriptors. 810 * 811 * <p>This is an asynchronous operation. Once service discovery is completed, 812 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is 813 * triggered. If the discovery was successful, the remote services can be 814 * retrieved using the {@link #getServices} function. 815 * 816 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 817 * 818 * @return true, if the remote service discovery has been started 819 */ 820 public boolean discoverServices() { 821 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); 822 if (mService == null || mClientIf == 0) return false; 823 824 mServices.clear(); 825 826 try { 827 mService.discoverServices(mClientIf, mDevice.getAddress()); 828 } catch (RemoteException e) { 829 Log.e(TAG,"",e); 830 return false; 831 } 832 833 return true; 834 } 835 836 /** 837 * Returns a list of GATT services offered by the remote device. 838 * 839 * <p>This function requires that service discovery has been completed 840 * for the given device. 841 * 842 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 843 * 844 * @return List of services on the remote device. Returns an empty list 845 * if service discovery has not yet been performed. 846 */ 847 public List<BluetoothGattService> getServices() { 848 List<BluetoothGattService> result = 849 new ArrayList<BluetoothGattService>(); 850 851 for (BluetoothGattService service : mServices) { 852 if (service.getDevice().equals(mDevice)) { 853 result.add(service); 854 } 855 } 856 857 return result; 858 } 859 860 /** 861 * Returns a {@link BluetoothGattService}, if the requested UUID is 862 * supported by the remote device. 863 * 864 * <p>This function requires that service discovery has been completed 865 * for the given device. 866 * 867 * <p>If multiple instances of the same service (as identified by UUID) 868 * exist, the first instance of the service is returned. 869 * 870 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 871 * 872 * @param uuid UUID of the requested service 873 * @return BluetoothGattService if supported, or null if the requested 874 * service is not offered by the remote device. 875 */ 876 public BluetoothGattService getService(UUID uuid) { 877 for (BluetoothGattService service : mServices) { 878 if (service.getDevice().equals(mDevice) && 879 service.getUuid().equals(uuid)) { 880 return service; 881 } 882 } 883 884 return null; 885 } 886 887 /** 888 * Reads the requested characteristic from the associated remote device. 889 * 890 * <p>This is an asynchronous operation. The result of the read operation 891 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} 892 * callback. 893 * 894 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 895 * 896 * @param characteristic Characteristic to read from the remote device 897 * @return true, if the read operation was initiated successfully 898 */ 899 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 900 if ((characteristic.getProperties() & 901 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false; 902 903 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); 904 if (mService == null || mClientIf == 0) return false; 905 906 BluetoothGattService service = characteristic.getService(); 907 if (service == null) return false; 908 909 BluetoothDevice device = service.getDevice(); 910 if (device == null) return false; 911 912 try { 913 mService.readCharacteristic(mClientIf, device.getAddress(), 914 service.getType(), service.getInstanceId(), 915 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 916 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); 917 } catch (RemoteException e) { 918 Log.e(TAG,"",e); 919 return false; 920 } 921 922 return true; 923 } 924 925 /** 926 * Writes a given characteristic and its values to the associated remote device. 927 * 928 * <p>Once the write operation has been completed, the 929 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, 930 * reporting the result of the operation. 931 * 932 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 933 * 934 * @param characteristic Characteristic to write on the remote device 935 * @return true, if the write operation was initiated successfully 936 */ 937 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 938 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 939 && (characteristic.getProperties() & 940 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false; 941 942 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); 943 if (mService == null || mClientIf == 0) return false; 944 945 BluetoothGattService service = characteristic.getService(); 946 if (service == null) return false; 947 948 BluetoothDevice device = service.getDevice(); 949 if (device == null) return false; 950 951 try { 952 mService.writeCharacteristic(mClientIf, device.getAddress(), 953 service.getType(), service.getInstanceId(), 954 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 955 new ParcelUuid(characteristic.getUuid()), 956 characteristic.getWriteType(), AUTHENTICATION_NONE, 957 characteristic.getValue()); 958 } catch (RemoteException e) { 959 Log.e(TAG,"",e); 960 return false; 961 } 962 963 return true; 964 } 965 966 /** 967 * Reads the value for a given descriptor from the associated remote device. 968 * 969 * <p>Once the read operation has been completed, the 970 * {@link BluetoothGattCallback#onDescriptorRead} callback is 971 * triggered, signaling the result of the operation. 972 * 973 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 974 * 975 * @param descriptor Descriptor value to read from the remote device 976 * @return true, if the read operation was initiated successfully 977 */ 978 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 979 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); 980 if (mService == null || mClientIf == 0) return false; 981 982 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 983 if (characteristic == null) return false; 984 985 BluetoothGattService service = characteristic.getService(); 986 if (service == null) return false; 987 988 BluetoothDevice device = service.getDevice(); 989 if (device == null) return false; 990 991 try { 992 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), 993 service.getInstanceId(), new ParcelUuid(service.getUuid()), 994 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), 995 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), 996 AUTHENTICATION_NONE); 997 } catch (RemoteException e) { 998 Log.e(TAG,"",e); 999 return false; 1000 } 1001 1002 return true; 1003 } 1004 1005 /** 1006 * Write the value of a given descriptor to the associated remote device. 1007 * 1008 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is 1009 * triggered to report the result of the write operation. 1010 * 1011 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1012 * 1013 * @param descriptor Descriptor to write to the associated remote device 1014 * @return true, if the write operation was initiated successfully 1015 */ 1016 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 1017 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); 1018 if (mService == null || mClientIf == 0) return false; 1019 1020 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 1021 if (characteristic == null) return false; 1022 1023 BluetoothGattService service = characteristic.getService(); 1024 if (service == null) return false; 1025 1026 BluetoothDevice device = service.getDevice(); 1027 if (device == null) return false; 1028 1029 try { 1030 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), 1031 service.getInstanceId(), new ParcelUuid(service.getUuid()), 1032 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()), 1033 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()), 1034 characteristic.getWriteType(), AUTHENTICATION_NONE, 1035 descriptor.getValue()); 1036 } catch (RemoteException e) { 1037 Log.e(TAG,"",e); 1038 return false; 1039 } 1040 1041 return true; 1042 } 1043 1044 /** 1045 * Initiates a reliable write transaction for a given remote device. 1046 * 1047 * <p>Once a reliable write transaction has been initiated, all calls 1048 * to {@link #writeCharacteristic} are sent to the remote device for 1049 * verification and queued up for atomic execution. The application will 1050 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback 1051 * in response to every {@link #writeCharacteristic} call and is responsible 1052 * for verifying if the value has been transmitted accurately. 1053 * 1054 * <p>After all characteristics have been queued up and verified, 1055 * {@link #executeReliableWrite} will execute all writes. If a characteristic 1056 * was not written correctly, calling {@link #abortReliableWrite} will 1057 * cancel the current transaction without commiting any values on the 1058 * remote device. 1059 * 1060 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1061 * 1062 * @return true, if the reliable write transaction has been initiated 1063 */ 1064 public boolean beginReliableWrite() { 1065 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); 1066 if (mService == null || mClientIf == 0) return false; 1067 1068 try { 1069 mService.beginReliableWrite(mClientIf, mDevice.getAddress()); 1070 } catch (RemoteException e) { 1071 Log.e(TAG,"",e); 1072 return false; 1073 } 1074 1075 return true; 1076 } 1077 1078 /** 1079 * Executes a reliable write transaction for a given remote device. 1080 * 1081 * <p>This function will commit all queued up characteristic write 1082 * operations for a given remote device. 1083 * 1084 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is 1085 * invoked to indicate whether the transaction has been executed correctly. 1086 * 1087 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1088 * 1089 * @return true, if the request to execute the transaction has been sent 1090 */ 1091 public boolean executeReliableWrite() { 1092 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); 1093 if (mService == null || mClientIf == 0) return false; 1094 1095 try { 1096 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); 1097 } catch (RemoteException e) { 1098 Log.e(TAG,"",e); 1099 return false; 1100 } 1101 1102 return true; 1103 } 1104 1105 /** 1106 * Cancels a reliable write transaction for a given device. 1107 * 1108 * <p>Calling this function will discard all queued characteristic write 1109 * operations for a given remote device. 1110 * 1111 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1112 */ 1113 public void abortReliableWrite() { 1114 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); 1115 if (mService == null || mClientIf == 0) return; 1116 1117 try { 1118 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); 1119 } catch (RemoteException e) { 1120 Log.e(TAG,"",e); 1121 } 1122 } 1123 1124 /** 1125 * @deprecated Use {@link #abortReliableWrite()} 1126 */ 1127 public void abortReliableWrite(BluetoothDevice mDevice) { 1128 abortReliableWrite(); 1129 } 1130 1131 /** 1132 * Enable or disable notifications/indications for a given characteristic. 1133 * 1134 * <p>Once notifications are enabled for a characteristic, a 1135 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be 1136 * triggered if the remote device indicates that the given characteristic 1137 * has changed. 1138 * 1139 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1140 * 1141 * @param characteristic The characteristic for which to enable notifications 1142 * @param enable Set to true to enable notifications/indications 1143 * @return true, if the requested notification status was set successfully 1144 */ 1145 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, 1146 boolean enable) { 1147 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() 1148 + " enable: " + enable); 1149 if (mService == null || mClientIf == 0) return false; 1150 1151 BluetoothGattService service = characteristic.getService(); 1152 if (service == null) return false; 1153 1154 BluetoothDevice device = service.getDevice(); 1155 if (device == null) return false; 1156 1157 try { 1158 mService.registerForNotification(mClientIf, device.getAddress(), 1159 service.getType(), service.getInstanceId(), 1160 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 1161 new ParcelUuid(characteristic.getUuid()), 1162 enable); 1163 } catch (RemoteException e) { 1164 Log.e(TAG,"",e); 1165 return false; 1166 } 1167 1168 return true; 1169 } 1170 1171 /** 1172 * Clears the internal cache and forces a refresh of the services from the 1173 * remote device. 1174 * @hide 1175 */ 1176 public boolean refresh() { 1177 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); 1178 if (mService == null || mClientIf == 0) return false; 1179 1180 try { 1181 mService.refreshDevice(mClientIf, mDevice.getAddress()); 1182 } catch (RemoteException e) { 1183 Log.e(TAG,"",e); 1184 return false; 1185 } 1186 1187 return true; 1188 } 1189 1190 /** 1191 * Read the RSSI for a connected remote device. 1192 * 1193 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be 1194 * invoked when the RSSI value has been read. 1195 * 1196 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 1197 * 1198 * @return true, if the RSSI value has been requested successfully 1199 */ 1200 public boolean readRemoteRssi() { 1201 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); 1202 if (mService == null || mClientIf == 0) return false; 1203 1204 try { 1205 mService.readRemoteRssi(mClientIf, mDevice.getAddress()); 1206 } catch (RemoteException e) { 1207 Log.e(TAG,"",e); 1208 return false; 1209 } 1210 1211 return true; 1212 } 1213 1214 /** 1215 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1216 * with {@link BluetoothProfile#GATT} as argument 1217 * 1218 * @throws UnsupportedOperationException 1219 */ 1220 @Override 1221 public int getConnectionState(BluetoothDevice device) { 1222 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 1223 } 1224 1225 /** 1226 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1227 * with {@link BluetoothProfile#GATT} as argument 1228 * 1229 * @throws UnsupportedOperationException 1230 */ 1231 @Override 1232 public List<BluetoothDevice> getConnectedDevices() { 1233 throw new UnsupportedOperationException 1234 ("Use BluetoothManager#getConnectedDevices instead."); 1235 } 1236 1237 /** 1238 * Not supported - please use 1239 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 1240 * with {@link BluetoothProfile#GATT} as first argument 1241 * 1242 * @throws UnsupportedOperationException 1243 */ 1244 @Override 1245 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1246 throw new UnsupportedOperationException 1247 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 1248 } 1249} 1250