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.annotation.RequiresNoPermission; 20 import android.annotation.RequiresPermission; 21 import android.annotation.SuppressLint; 22 import android.bluetooth.annotations.RequiresBluetoothConnectPermission; 23 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.content.AttributionSource; 26 import android.os.Build; 27 import android.os.Handler; 28 import android.os.ParcelUuid; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.UUID; 35 36 /** 37 * Public API for the Bluetooth GATT Profile. 38 * 39 * <p>This class provides Bluetooth GATT functionality to enable communication 40 * with Bluetooth Smart or Smart Ready devices. 41 * 42 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} 43 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. 44 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE 45 * scan process. 46 */ 47 public final class BluetoothGatt implements BluetoothProfile { 48 private static final String TAG = "BluetoothGatt"; 49 private static final boolean DBG = true; 50 private static final boolean VDBG = false; 51 52 @UnsupportedAppUsage 53 private IBluetoothGatt mService; 54 @UnsupportedAppUsage 55 private volatile BluetoothGattCallback mCallback; 56 private Handler mHandler; 57 @UnsupportedAppUsage 58 private int mClientIf; 59 private BluetoothDevice mDevice; 60 @UnsupportedAppUsage 61 private boolean mAutoConnect; 62 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 63 private int mAuthRetryState; 64 private int mConnState; 65 private final Object mStateLock = new Object(); 66 private final Object mDeviceBusyLock = new Object(); 67 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 68 private Boolean mDeviceBusy = false; 69 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 70 private int mTransport; 71 private int mPhy; 72 private boolean mOpportunistic; 73 private final AttributionSource mAttributionSource; 74 75 private static final int AUTH_RETRY_STATE_IDLE = 0; 76 private static final int AUTH_RETRY_STATE_NO_MITM = 1; 77 private static final int AUTH_RETRY_STATE_MITM = 2; 78 79 private static final int CONN_STATE_IDLE = 0; 80 private static final int CONN_STATE_CONNECTING = 1; 81 private static final int CONN_STATE_CONNECTED = 2; 82 private static final int CONN_STATE_DISCONNECTING = 3; 83 private static final int CONN_STATE_CLOSED = 4; 84 85 private List<BluetoothGattService> mServices; 86 87 /** A GATT operation completed successfully */ 88 public static final int GATT_SUCCESS = 0; 89 90 /** GATT read operation is not permitted */ 91 public static final int GATT_READ_NOT_PERMITTED = 0x2; 92 93 /** GATT write operation is not permitted */ 94 public static final int GATT_WRITE_NOT_PERMITTED = 0x3; 95 96 /** Insufficient authentication for a given operation */ 97 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5; 98 99 /** The given request is not supported */ 100 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6; 101 102 /** Insufficient encryption for a given operation */ 103 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf; 104 105 /** A read or write operation was requested with an invalid offset */ 106 public static final int GATT_INVALID_OFFSET = 0x7; 107 108 /** A write operation exceeds the maximum length of the attribute */ 109 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; 110 111 /** A remote device connection is congested. */ 112 public static final int GATT_CONNECTION_CONGESTED = 0x8f; 113 114 /** A GATT operation failed, errors other than the above */ 115 public static final int GATT_FAILURE = 0x101; 116 117 /** 118 * Connection parameter update - Use the connection parameters recommended by the 119 * Bluetooth SIG. This is the default value if no connection parameter update 120 * is requested. 121 */ 122 public static final int CONNECTION_PRIORITY_BALANCED = 0; 123 124 /** 125 * Connection parameter update - Request a high priority, low latency connection. 126 * An application should only request high priority connection parameters to transfer large 127 * amounts of data over LE quickly. Once the transfer is complete, the application should 128 * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce 129 * energy use. 130 */ 131 public static final int CONNECTION_PRIORITY_HIGH = 1; 132 133 /** Connection parameter update - Request low power, reduced data rate connection parameters. */ 134 public static final int CONNECTION_PRIORITY_LOW_POWER = 2; 135 136 /** 137 * No authentication required. 138 * 139 * @hide 140 */ 141 /*package*/ static final int AUTHENTICATION_NONE = 0; 142 143 /** 144 * Authentication requested; no person-in-the-middle protection required. 145 * 146 * @hide 147 */ 148 /*package*/ static final int AUTHENTICATION_NO_MITM = 1; 149 150 /** 151 * Authentication with person-in-the-middle protection requested. 152 * 153 * @hide 154 */ 155 /*package*/ static final int AUTHENTICATION_MITM = 2; 156 157 /** 158 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. 159 */ 160 @SuppressLint("AndroidFrameworkBluetoothPermission") 161 private final IBluetoothGattCallback mBluetoothGattCallback = 162 new IBluetoothGattCallback.Stub() { 163 /** 164 * Application interface registered - app is ready to go 165 * @hide 166 */ 167 @Override 168 @SuppressLint("AndroidFrameworkRequiresPermission") 169 public void onClientRegistered(int status, int clientIf) { 170 if (DBG) { 171 Log.d(TAG, "onClientRegistered() - status=" + status 172 + " clientIf=" + clientIf); 173 } 174 if (VDBG) { 175 synchronized (mStateLock) { 176 if (mConnState != CONN_STATE_CONNECTING) { 177 Log.e(TAG, "Bad connection state: " + mConnState); 178 } 179 } 180 } 181 mClientIf = clientIf; 182 if (status != GATT_SUCCESS) { 183 runOrQueueCallback(new Runnable() { 184 @Override 185 public void run() { 186 final BluetoothGattCallback callback = mCallback; 187 if (callback != null) { 188 callback.onConnectionStateChange(BluetoothGatt.this, 189 GATT_FAILURE, 190 BluetoothProfile.STATE_DISCONNECTED); 191 } 192 } 193 }); 194 195 synchronized (mStateLock) { 196 mConnState = CONN_STATE_IDLE; 197 } 198 return; 199 } 200 try { 201 mService.clientConnect(mClientIf, mDevice.getAddress(), 202 !mAutoConnect, mTransport, mOpportunistic, 203 mPhy, mAttributionSource); // autoConnect is inverse of "isDirect" 204 } catch (RemoteException e) { 205 Log.e(TAG, "", e); 206 } 207 } 208 209 /** 210 * Phy update callback 211 * @hide 212 */ 213 @Override 214 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { 215 if (DBG) { 216 Log.d(TAG, "onPhyUpdate() - status=" + status 217 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); 218 } 219 if (!address.equals(mDevice.getAddress())) { 220 return; 221 } 222 223 runOrQueueCallback(new Runnable() { 224 @Override 225 public void run() { 226 final BluetoothGattCallback callback = mCallback; 227 if (callback != null) { 228 callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); 229 } 230 } 231 }); 232 } 233 234 /** 235 * Phy read callback 236 * @hide 237 */ 238 @Override 239 public void onPhyRead(String address, int txPhy, int rxPhy, int status) { 240 if (DBG) { 241 Log.d(TAG, "onPhyRead() - status=" + status 242 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); 243 } 244 if (!address.equals(mDevice.getAddress())) { 245 return; 246 } 247 248 runOrQueueCallback(new Runnable() { 249 @Override 250 public void run() { 251 final BluetoothGattCallback callback = mCallback; 252 if (callback != null) { 253 callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); 254 } 255 } 256 }); 257 } 258 259 /** 260 * Client connection state changed 261 * @hide 262 */ 263 @Override 264 public void onClientConnectionState(int status, int clientIf, 265 boolean connected, String address) { 266 if (DBG) { 267 Log.d(TAG, "onClientConnectionState() - status=" + status 268 + " clientIf=" + clientIf + " device=" + address); 269 } 270 if (!address.equals(mDevice.getAddress())) { 271 return; 272 } 273 int profileState = connected ? BluetoothProfile.STATE_CONNECTED : 274 BluetoothProfile.STATE_DISCONNECTED; 275 276 runOrQueueCallback(new Runnable() { 277 @Override 278 public void run() { 279 final BluetoothGattCallback callback = mCallback; 280 if (callback != null) { 281 callback.onConnectionStateChange(BluetoothGatt.this, status, 282 profileState); 283 } 284 } 285 }); 286 287 synchronized (mStateLock) { 288 if (connected) { 289 mConnState = CONN_STATE_CONNECTED; 290 } else { 291 mConnState = CONN_STATE_IDLE; 292 } 293 } 294 295 synchronized (mDeviceBusyLock) { 296 mDeviceBusy = false; 297 } 298 } 299 300 /** 301 * Remote search has been completed. 302 * The internal object structure should now reflect the state 303 * of the remote device database. Let the application know that 304 * we are done at this point. 305 * @hide 306 */ 307 @Override 308 public void onSearchComplete(String address, List<BluetoothGattService> services, 309 int status) { 310 if (DBG) { 311 Log.d(TAG, 312 "onSearchComplete() = Device=" + address + " Status=" + status); 313 } 314 if (!address.equals(mDevice.getAddress())) { 315 return; 316 } 317 318 for (BluetoothGattService s : services) { 319 //services we receive don't have device set properly. 320 s.setDevice(mDevice); 321 } 322 323 mServices.addAll(services); 324 325 // Fix references to included services, as they doesn't point to right objects. 326 for (BluetoothGattService fixedService : mServices) { 327 ArrayList<BluetoothGattService> includedServices = 328 new ArrayList(fixedService.getIncludedServices()); 329 fixedService.getIncludedServices().clear(); 330 331 for (BluetoothGattService brokenRef : includedServices) { 332 BluetoothGattService includedService = getService(mDevice, 333 brokenRef.getUuid(), brokenRef.getInstanceId()); 334 if (includedService != null) { 335 fixedService.addIncludedService(includedService); 336 } else { 337 Log.e(TAG, "Broken GATT database: can't find included service."); 338 } 339 } 340 } 341 342 runOrQueueCallback(new Runnable() { 343 @Override 344 public void run() { 345 final BluetoothGattCallback callback = mCallback; 346 if (callback != null) { 347 callback.onServicesDiscovered(BluetoothGatt.this, status); 348 } 349 } 350 }); 351 } 352 353 /** 354 * Remote characteristic has been read. 355 * Updates the internal value. 356 * @hide 357 */ 358 @Override 359 @SuppressLint("AndroidFrameworkRequiresPermission") 360 public void onCharacteristicRead(String address, int status, int handle, 361 byte[] value) { 362 if (VDBG) { 363 Log.d(TAG, "onCharacteristicRead() - Device=" + address 364 + " handle=" + handle + " Status=" + status); 365 } 366 367 if (!address.equals(mDevice.getAddress())) { 368 return; 369 } 370 371 synchronized (mDeviceBusyLock) { 372 mDeviceBusy = false; 373 } 374 375 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 376 || status == GATT_INSUFFICIENT_ENCRYPTION) 377 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 378 try { 379 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 380 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; 381 mService.readCharacteristic( 382 mClientIf, address, handle, authReq, mAttributionSource); 383 mAuthRetryState++; 384 return; 385 } catch (RemoteException e) { 386 Log.e(TAG, "", e); 387 } 388 } 389 390 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 391 392 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, 393 handle); 394 if (characteristic == null) { 395 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); 396 return; 397 } 398 399 runOrQueueCallback(new Runnable() { 400 @Override 401 public void run() { 402 final BluetoothGattCallback callback = mCallback; 403 if (callback != null) { 404 if (status == 0) characteristic.setValue(value); 405 callback.onCharacteristicRead(BluetoothGatt.this, characteristic, 406 status); 407 } 408 } 409 }); 410 } 411 412 /** 413 * Characteristic has been written to the remote device. 414 * Let the app know how we did... 415 * @hide 416 */ 417 @Override 418 @SuppressLint("AndroidFrameworkRequiresPermission") 419 public void onCharacteristicWrite(String address, int status, int handle) { 420 if (VDBG) { 421 Log.d(TAG, "onCharacteristicWrite() - Device=" + address 422 + " handle=" + handle + " Status=" + status); 423 } 424 425 if (!address.equals(mDevice.getAddress())) { 426 return; 427 } 428 429 synchronized (mDeviceBusyLock) { 430 mDeviceBusy = false; 431 } 432 433 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, 434 handle); 435 if (characteristic == null) return; 436 437 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 438 || status == GATT_INSUFFICIENT_ENCRYPTION) 439 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 440 try { 441 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 442 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; 443 mService.writeCharacteristic(mClientIf, address, handle, 444 characteristic.getWriteType(), authReq, 445 characteristic.getValue(), mAttributionSource); 446 mAuthRetryState++; 447 return; 448 } catch (RemoteException e) { 449 Log.e(TAG, "", e); 450 } 451 } 452 453 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 454 455 runOrQueueCallback(new Runnable() { 456 @Override 457 public void run() { 458 final BluetoothGattCallback callback = mCallback; 459 if (callback != null) { 460 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic, 461 status); 462 } 463 } 464 }); 465 } 466 467 /** 468 * Remote characteristic has been updated. 469 * Updates the internal value. 470 * @hide 471 */ 472 @Override 473 public void onNotify(String address, int handle, byte[] value) { 474 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); 475 476 if (!address.equals(mDevice.getAddress())) { 477 return; 478 } 479 480 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, 481 handle); 482 if (characteristic == null) return; 483 484 runOrQueueCallback(new Runnable() { 485 @Override 486 public void run() { 487 final BluetoothGattCallback callback = mCallback; 488 if (callback != null) { 489 characteristic.setValue(value); 490 callback.onCharacteristicChanged(BluetoothGatt.this, 491 characteristic); 492 } 493 } 494 }); 495 } 496 497 /** 498 * Descriptor has been read. 499 * @hide 500 */ 501 @Override 502 @SuppressLint("AndroidFrameworkRequiresPermission") 503 public void onDescriptorRead(String address, int status, int handle, byte[] value) { 504 if (VDBG) { 505 Log.d(TAG, 506 "onDescriptorRead() - Device=" + address + " handle=" + handle); 507 } 508 509 if (!address.equals(mDevice.getAddress())) { 510 return; 511 } 512 513 synchronized (mDeviceBusyLock) { 514 mDeviceBusy = false; 515 } 516 517 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); 518 if (descriptor == null) return; 519 520 521 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 522 || status == GATT_INSUFFICIENT_ENCRYPTION) 523 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 524 try { 525 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 526 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; 527 mService.readDescriptor( 528 mClientIf, address, handle, authReq, mAttributionSource); 529 mAuthRetryState++; 530 return; 531 } catch (RemoteException e) { 532 Log.e(TAG, "", e); 533 } 534 } 535 536 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 537 538 runOrQueueCallback(new Runnable() { 539 @Override 540 public void run() { 541 final BluetoothGattCallback callback = mCallback; 542 if (callback != null) { 543 if (status == 0) descriptor.setValue(value); 544 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status); 545 } 546 } 547 }); 548 } 549 550 /** 551 * Descriptor write operation complete. 552 * @hide 553 */ 554 @Override 555 @SuppressLint("AndroidFrameworkRequiresPermission") 556 public void onDescriptorWrite(String address, int status, int handle) { 557 if (VDBG) { 558 Log.d(TAG, 559 "onDescriptorWrite() - Device=" + address + " handle=" + handle); 560 } 561 562 if (!address.equals(mDevice.getAddress())) { 563 return; 564 } 565 566 synchronized (mDeviceBusyLock) { 567 mDeviceBusy = false; 568 } 569 570 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); 571 if (descriptor == null) return; 572 573 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 574 || status == GATT_INSUFFICIENT_ENCRYPTION) 575 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 576 try { 577 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 578 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; 579 mService.writeDescriptor(mClientIf, address, handle, 580 authReq, descriptor.getValue(), mAttributionSource); 581 mAuthRetryState++; 582 return; 583 } catch (RemoteException e) { 584 Log.e(TAG, "", e); 585 } 586 } 587 588 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 589 590 runOrQueueCallback(new Runnable() { 591 @Override 592 public void run() { 593 final BluetoothGattCallback callback = mCallback; 594 if (callback != null) { 595 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); 596 } 597 } 598 }); 599 } 600 601 /** 602 * Prepared write transaction completed (or aborted) 603 * @hide 604 */ 605 @Override 606 public void onExecuteWrite(String address, int status) { 607 if (VDBG) { 608 Log.d(TAG, "onExecuteWrite() - Device=" + address 609 + " status=" + status); 610 } 611 if (!address.equals(mDevice.getAddress())) { 612 return; 613 } 614 615 synchronized (mDeviceBusyLock) { 616 mDeviceBusy = false; 617 } 618 619 runOrQueueCallback(new Runnable() { 620 @Override 621 public void run() { 622 final BluetoothGattCallback callback = mCallback; 623 if (callback != null) { 624 callback.onReliableWriteCompleted(BluetoothGatt.this, status); 625 } 626 } 627 }); 628 } 629 630 /** 631 * Remote device RSSI has been read 632 * @hide 633 */ 634 @Override 635 public void onReadRemoteRssi(String address, int rssi, int status) { 636 if (VDBG) { 637 Log.d(TAG, "onReadRemoteRssi() - Device=" + address 638 + " rssi=" + rssi + " status=" + status); 639 } 640 if (!address.equals(mDevice.getAddress())) { 641 return; 642 } 643 runOrQueueCallback(new Runnable() { 644 @Override 645 public void run() { 646 final BluetoothGattCallback callback = mCallback; 647 if (callback != null) { 648 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); 649 } 650 } 651 }); 652 } 653 654 /** 655 * Callback invoked when the MTU for a given connection changes 656 * @hide 657 */ 658 @Override 659 public void onConfigureMTU(String address, int mtu, int status) { 660 if (DBG) { 661 Log.d(TAG, "onConfigureMTU() - Device=" + address 662 + " mtu=" + mtu + " status=" + status); 663 } 664 if (!address.equals(mDevice.getAddress())) { 665 return; 666 } 667 668 runOrQueueCallback(new Runnable() { 669 @Override 670 public void run() { 671 final BluetoothGattCallback callback = mCallback; 672 if (callback != null) { 673 callback.onMtuChanged(BluetoothGatt.this, mtu, status); 674 } 675 } 676 }); 677 } 678 679 /** 680 * Callback invoked when the given connection is updated 681 * @hide 682 */ 683 @Override 684 public void onConnectionUpdated(String address, int interval, int latency, 685 int timeout, int status) { 686 if (DBG) { 687 Log.d(TAG, "onConnectionUpdated() - Device=" + address 688 + " interval=" + interval + " latency=" + latency 689 + " timeout=" + timeout + " status=" + status); 690 } 691 if (!address.equals(mDevice.getAddress())) { 692 return; 693 } 694 695 runOrQueueCallback(new Runnable() { 696 @Override 697 public void run() { 698 final BluetoothGattCallback callback = mCallback; 699 if (callback != null) { 700 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency, 701 timeout, status); 702 } 703 } 704 }); 705 } 706 707 /** 708 * Callback invoked when service changed event is received 709 * @hide 710 */ 711 @Override 712 public void onServiceChanged(String address) { 713 if (DBG) { 714 Log.d(TAG, "onServiceChanged() - Device=" + address); 715 } 716 717 if (!address.equals(mDevice.getAddress())) { 718 return; 719 } 720 721 runOrQueueCallback(new Runnable() { 722 @Override 723 public void run() { 724 final BluetoothGattCallback callback = mCallback; 725 if (callback != null) { 726 callback.onServiceChanged(BluetoothGatt.this); 727 } 728 } 729 }); 730 } 731 }; 732 BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport, boolean opportunistic, int phy, AttributionSource attributionSource)733 /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport, 734 boolean opportunistic, int phy, AttributionSource attributionSource) { 735 mService = iGatt; 736 mDevice = device; 737 mTransport = transport; 738 mPhy = phy; 739 mOpportunistic = opportunistic; 740 mAttributionSource = attributionSource; 741 mServices = new ArrayList<BluetoothGattService>(); 742 743 mConnState = CONN_STATE_IDLE; 744 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 745 } 746 747 /** 748 * Close this Bluetooth GATT client. 749 * 750 * Application should call this method as early as possible after it is done with 751 * this GATT client. 752 */ 753 @RequiresBluetoothConnectPermission 754 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) close()755 public void close() { 756 if (DBG) Log.d(TAG, "close()"); 757 758 unregisterApp(); 759 mConnState = CONN_STATE_CLOSED; 760 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 761 } 762 763 /** 764 * Returns a service by UUID, instance and type. 765 * 766 * @hide 767 */ getService(BluetoothDevice device, UUID uuid, int instanceId)768 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, 769 int instanceId) { 770 for (BluetoothGattService svc : mServices) { 771 if (svc.getDevice().equals(device) 772 && svc.getInstanceId() == instanceId 773 && svc.getUuid().equals(uuid)) { 774 return svc; 775 } 776 } 777 return null; 778 } 779 780 781 /** 782 * Returns a characteristic with id equal to instanceId. 783 * 784 * @hide 785 */ getCharacteristicById(BluetoothDevice device, int instanceId)786 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, 787 int instanceId) { 788 for (BluetoothGattService svc : mServices) { 789 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 790 if (charac.getInstanceId() == instanceId) { 791 return charac; 792 } 793 } 794 } 795 return null; 796 } 797 798 /** 799 * Returns a descriptor with id equal to instanceId. 800 * 801 * @hide 802 */ getDescriptorById(BluetoothDevice device, int instanceId)803 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) { 804 for (BluetoothGattService svc : mServices) { 805 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 806 for (BluetoothGattDescriptor desc : charac.getDescriptors()) { 807 if (desc.getInstanceId() == instanceId) { 808 return desc; 809 } 810 } 811 } 812 } 813 return null; 814 } 815 816 /** 817 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable 818 * immediately if no Handler was provided. 819 */ runOrQueueCallback(final Runnable cb)820 private void runOrQueueCallback(final Runnable cb) { 821 if (mHandler == null) { 822 try { 823 cb.run(); 824 } catch (Exception ex) { 825 Log.w(TAG, "Unhandled exception in callback", ex); 826 } 827 } else { 828 mHandler.post(cb); 829 } 830 } 831 832 /** 833 * Register an application callback to start using GATT. 834 * 835 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} 836 * is used to notify success or failure if the function returns true. 837 * 838 * @param callback GATT callback handler that will receive asynchronous callbacks. 839 * @return If true, the callback will be called to notify success or failure, false on immediate 840 * error 841 */ 842 @RequiresLegacyBluetoothPermission 843 @RequiresBluetoothConnectPermission 844 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) registerApp(BluetoothGattCallback callback, Handler handler)845 private boolean registerApp(BluetoothGattCallback callback, Handler handler) { 846 return registerApp(callback, handler, false); 847 } 848 849 /** 850 * Register an application callback to start using GATT. 851 * 852 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} 853 * is used to notify success or failure if the function returns true. 854 * 855 * @param callback GATT callback handler that will receive asynchronous callbacks. 856 * @param eatt_support indicate to allow for eatt support 857 * @return If true, the callback will be called to notify success or failure, false on immediate 858 * error 859 * @hide 860 */ 861 @RequiresLegacyBluetoothPermission 862 @RequiresBluetoothConnectPermission 863 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) registerApp(BluetoothGattCallback callback, Handler handler, boolean eatt_support)864 private boolean registerApp(BluetoothGattCallback callback, Handler handler, 865 boolean eatt_support) { 866 if (DBG) Log.d(TAG, "registerApp()"); 867 if (mService == null) return false; 868 869 mCallback = callback; 870 mHandler = handler; 871 UUID uuid = UUID.randomUUID(); 872 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); 873 874 try { 875 mService.registerClient( 876 new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource); 877 } catch (RemoteException e) { 878 Log.e(TAG, "", e); 879 return false; 880 } 881 882 return true; 883 } 884 885 /** 886 * Unregister the current application and callbacks. 887 */ 888 @UnsupportedAppUsage 889 @RequiresBluetoothConnectPermission 890 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) unregisterApp()891 private void unregisterApp() { 892 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); 893 if (mService == null || mClientIf == 0) return; 894 895 try { 896 mCallback = null; 897 mService.unregisterClient(mClientIf, mAttributionSource); 898 mClientIf = 0; 899 } catch (RemoteException e) { 900 Log.e(TAG, "", e); 901 } 902 } 903 904 /** 905 * Initiate a connection to a Bluetooth GATT capable device. 906 * 907 * <p>The connection may not be established right away, but will be 908 * completed when the remote device is available. A 909 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be 910 * invoked when the connection state changes as a result of this function. 911 * 912 * <p>The autoConnect parameter determines whether to actively connect to 913 * the remote device, or rather passively scan and finalize the connection 914 * when the remote device is in range/available. Generally, the first ever 915 * connection to a device should be direct (autoConnect set to false) and 916 * subsequent connections to known devices should be invoked with the 917 * autoConnect parameter set to true. 918 * 919 * @param device Remote device to connect to 920 * @param autoConnect Whether to directly connect to the remote device (false) or to 921 * automatically connect as soon as the remote device becomes available (true). 922 * @return true, if the connection attempt was initiated successfully 923 */ 924 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 925 @RequiresLegacyBluetoothPermission 926 @RequiresBluetoothConnectPermission 927 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler)928 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, 929 Handler handler) { 930 if (DBG) { 931 Log.d(TAG, 932 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); 933 } 934 synchronized (mStateLock) { 935 if (mConnState != CONN_STATE_IDLE) { 936 throw new IllegalStateException("Not idle"); 937 } 938 mConnState = CONN_STATE_CONNECTING; 939 } 940 941 mAutoConnect = autoConnect; 942 943 if (!registerApp(callback, handler)) { 944 synchronized (mStateLock) { 945 mConnState = CONN_STATE_IDLE; 946 } 947 Log.e(TAG, "Failed to register callback"); 948 return false; 949 } 950 951 // The connection will continue in the onClientRegistered callback 952 return true; 953 } 954 955 /** 956 * Disconnects an established connection, or cancels a connection attempt 957 * currently in progress. 958 */ 959 @RequiresLegacyBluetoothPermission 960 @RequiresBluetoothConnectPermission 961 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) disconnect()962 public void disconnect() { 963 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); 964 if (mService == null || mClientIf == 0) return; 965 966 try { 967 mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource); 968 } catch (RemoteException e) { 969 Log.e(TAG, "", e); 970 } 971 } 972 973 /** 974 * Connect back to remote device. 975 * 976 * <p>This method is used to re-connect to a remote device after the 977 * connection has been dropped. If the device is not in range, the 978 * re-connection will be triggered once the device is back in range. 979 * 980 * @return true, if the connection attempt was initiated successfully 981 */ 982 @RequiresBluetoothConnectPermission 983 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect()984 public boolean connect() { 985 try { 986 // autoConnect is inverse of "isDirect" 987 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, 988 mOpportunistic, mPhy, mAttributionSource); 989 return true; 990 } catch (RemoteException e) { 991 Log.e(TAG, "", e); 992 return false; 993 } 994 } 995 996 /** 997 * Set the preferred connection PHY for this app. Please note that this is just a 998 * recommendation, whether the PHY change will happen depends on other applications preferences, 999 * local and remote controller capabilities. Controller can override these settings. 1000 * <p> 1001 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even 1002 * if no PHY change happens. It is also triggered when remote device updates the PHY. 1003 * 1004 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link 1005 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 1006 * BluetoothDevice#PHY_LE_CODED_MASK}. 1007 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link 1008 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 1009 * BluetoothDevice#PHY_LE_CODED_MASK}. 1010 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one 1011 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or 1012 * {@link BluetoothDevice#PHY_OPTION_S8} 1013 */ 1014 @RequiresBluetoothConnectPermission 1015 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setPreferredPhy(int txPhy, int rxPhy, int phyOptions)1016 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { 1017 try { 1018 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, 1019 phyOptions, mAttributionSource); 1020 } catch (RemoteException e) { 1021 Log.e(TAG, "", e); 1022 } 1023 } 1024 1025 /** 1026 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned 1027 * in {@link BluetoothGattCallback#onPhyRead} 1028 */ 1029 @RequiresBluetoothConnectPermission 1030 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readPhy()1031 public void readPhy() { 1032 try { 1033 mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource); 1034 } catch (RemoteException e) { 1035 Log.e(TAG, "", e); 1036 } 1037 } 1038 1039 /** 1040 * Return the remote bluetooth device this GATT client targets to 1041 * 1042 * @return remote bluetooth device 1043 */ 1044 @RequiresNoPermission getDevice()1045 public BluetoothDevice getDevice() { 1046 return mDevice; 1047 } 1048 1049 /** 1050 * Discovers services offered by a remote device as well as their 1051 * characteristics and descriptors. 1052 * 1053 * <p>This is an asynchronous operation. Once service discovery is completed, 1054 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is 1055 * triggered. If the discovery was successful, the remote services can be 1056 * retrieved using the {@link #getServices} function. 1057 * 1058 * @return true, if the remote service discovery has been started 1059 */ 1060 @RequiresLegacyBluetoothPermission 1061 @RequiresBluetoothConnectPermission 1062 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) discoverServices()1063 public boolean discoverServices() { 1064 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); 1065 if (mService == null || mClientIf == 0) return false; 1066 1067 mServices.clear(); 1068 1069 try { 1070 mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource); 1071 } catch (RemoteException e) { 1072 Log.e(TAG, "", e); 1073 return false; 1074 } 1075 1076 return true; 1077 } 1078 1079 /** 1080 * Discovers a service by UUID. This is exposed only for passing PTS tests. 1081 * It should never be used by real applications. The service is not searched 1082 * for characteristics and descriptors, or returned in any callback. 1083 * 1084 * @return true, if the remote service discovery has been started 1085 * @hide 1086 */ 1087 @RequiresLegacyBluetoothPermission 1088 @RequiresBluetoothConnectPermission 1089 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) discoverServiceByUuid(UUID uuid)1090 public boolean discoverServiceByUuid(UUID uuid) { 1091 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress()); 1092 if (mService == null || mClientIf == 0) return false; 1093 1094 mServices.clear(); 1095 1096 try { 1097 mService.discoverServiceByUuid( 1098 mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); 1099 } catch (RemoteException e) { 1100 Log.e(TAG, "", e); 1101 return false; 1102 } 1103 return true; 1104 } 1105 1106 /** 1107 * Returns a list of GATT services offered by the remote device. 1108 * 1109 * <p>This function requires that service discovery has been completed 1110 * for the given device. 1111 * 1112 * @return List of services on the remote device. Returns an empty list if service discovery has 1113 * not yet been performed. 1114 */ 1115 @RequiresLegacyBluetoothPermission 1116 @RequiresNoPermission getServices()1117 public List<BluetoothGattService> getServices() { 1118 List<BluetoothGattService> result = 1119 new ArrayList<BluetoothGattService>(); 1120 1121 for (BluetoothGattService service : mServices) { 1122 if (service.getDevice().equals(mDevice)) { 1123 result.add(service); 1124 } 1125 } 1126 1127 return result; 1128 } 1129 1130 /** 1131 * Returns a {@link BluetoothGattService}, if the requested UUID is 1132 * supported by the remote device. 1133 * 1134 * <p>This function requires that service discovery has been completed 1135 * for the given device. 1136 * 1137 * <p>If multiple instances of the same service (as identified by UUID) 1138 * exist, the first instance of the service is returned. 1139 * 1140 * @param uuid UUID of the requested service 1141 * @return BluetoothGattService if supported, or null if the requested service is not offered by 1142 * the remote device. 1143 */ 1144 @RequiresLegacyBluetoothPermission 1145 @RequiresNoPermission getService(UUID uuid)1146 public BluetoothGattService getService(UUID uuid) { 1147 for (BluetoothGattService service : mServices) { 1148 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { 1149 return service; 1150 } 1151 } 1152 1153 return null; 1154 } 1155 1156 /** 1157 * Reads the requested characteristic from the associated remote device. 1158 * 1159 * <p>This is an asynchronous operation. The result of the read operation 1160 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} 1161 * callback. 1162 * 1163 * @param characteristic Characteristic to read from the remote device 1164 * @return true, if the read operation was initiated successfully 1165 */ 1166 @RequiresLegacyBluetoothPermission 1167 @RequiresBluetoothConnectPermission 1168 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readCharacteristic(BluetoothGattCharacteristic characteristic)1169 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 1170 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) { 1171 return false; 1172 } 1173 1174 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); 1175 if (mService == null || mClientIf == 0) return false; 1176 1177 BluetoothGattService service = characteristic.getService(); 1178 if (service == null) return false; 1179 1180 BluetoothDevice device = service.getDevice(); 1181 if (device == null) return false; 1182 1183 synchronized (mDeviceBusyLock) { 1184 if (mDeviceBusy) return false; 1185 mDeviceBusy = true; 1186 } 1187 1188 try { 1189 mService.readCharacteristic(mClientIf, device.getAddress(), 1190 characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); 1191 } catch (RemoteException e) { 1192 Log.e(TAG, "", e); 1193 mDeviceBusy = false; 1194 return false; 1195 } 1196 1197 return true; 1198 } 1199 1200 /** 1201 * Reads the characteristic using its UUID from the associated remote device. 1202 * 1203 * <p>This is an asynchronous operation. The result of the read operation 1204 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} 1205 * callback. 1206 * 1207 * @param uuid UUID of characteristic to read from the remote device 1208 * @return true, if the read operation was initiated successfully 1209 * @hide 1210 */ 1211 @RequiresLegacyBluetoothPermission 1212 @RequiresBluetoothConnectPermission 1213 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle)1214 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { 1215 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); 1216 if (mService == null || mClientIf == 0) return false; 1217 1218 synchronized (mDeviceBusyLock) { 1219 if (mDeviceBusy) return false; 1220 mDeviceBusy = true; 1221 } 1222 1223 try { 1224 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), 1225 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE, 1226 mAttributionSource); 1227 } catch (RemoteException e) { 1228 Log.e(TAG, "", e); 1229 mDeviceBusy = false; 1230 return false; 1231 } 1232 1233 return true; 1234 } 1235 1236 1237 /** 1238 * Writes a given characteristic and its values to the associated remote device. 1239 * 1240 * <p>Once the write operation has been completed, the 1241 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, 1242 * reporting the result of the operation. 1243 * 1244 * @param characteristic Characteristic to write on the remote device 1245 * @return true, if the write operation was initiated successfully 1246 */ 1247 @RequiresLegacyBluetoothPermission 1248 @RequiresBluetoothConnectPermission 1249 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) writeCharacteristic(BluetoothGattCharacteristic characteristic)1250 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 1251 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 1252 && (characteristic.getProperties() 1253 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { 1254 return false; 1255 } 1256 1257 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); 1258 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false; 1259 1260 BluetoothGattService service = characteristic.getService(); 1261 if (service == null) return false; 1262 1263 BluetoothDevice device = service.getDevice(); 1264 if (device == null) return false; 1265 1266 synchronized (mDeviceBusyLock) { 1267 if (mDeviceBusy) return false; 1268 mDeviceBusy = true; 1269 } 1270 1271 try { 1272 mService.writeCharacteristic(mClientIf, device.getAddress(), 1273 characteristic.getInstanceId(), characteristic.getWriteType(), 1274 AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource); 1275 } catch (RemoteException e) { 1276 Log.e(TAG, "", e); 1277 mDeviceBusy = false; 1278 return false; 1279 } 1280 1281 return true; 1282 } 1283 1284 /** 1285 * Reads the value for a given descriptor from the associated remote device. 1286 * 1287 * <p>Once the read operation has been completed, the 1288 * {@link BluetoothGattCallback#onDescriptorRead} callback is 1289 * triggered, signaling the result of the operation. 1290 * 1291 * @param descriptor Descriptor value to read from the remote device 1292 * @return true, if the read operation was initiated successfully 1293 */ 1294 @RequiresLegacyBluetoothPermission 1295 @RequiresBluetoothConnectPermission 1296 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readDescriptor(BluetoothGattDescriptor descriptor)1297 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 1298 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); 1299 if (mService == null || mClientIf == 0) return false; 1300 1301 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 1302 if (characteristic == null) return false; 1303 1304 BluetoothGattService service = characteristic.getService(); 1305 if (service == null) return false; 1306 1307 BluetoothDevice device = service.getDevice(); 1308 if (device == null) return false; 1309 1310 synchronized (mDeviceBusyLock) { 1311 if (mDeviceBusy) return false; 1312 mDeviceBusy = true; 1313 } 1314 1315 try { 1316 mService.readDescriptor(mClientIf, device.getAddress(), 1317 descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); 1318 } catch (RemoteException e) { 1319 Log.e(TAG, "", e); 1320 mDeviceBusy = false; 1321 return false; 1322 } 1323 1324 return true; 1325 } 1326 1327 /** 1328 * Write the value of a given descriptor to the associated remote device. 1329 * 1330 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is 1331 * triggered to report the result of the write operation. 1332 * 1333 * @param descriptor Descriptor to write to the associated remote device 1334 * @return true, if the write operation was initiated successfully 1335 */ 1336 @RequiresLegacyBluetoothPermission 1337 @RequiresBluetoothConnectPermission 1338 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) writeDescriptor(BluetoothGattDescriptor descriptor)1339 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 1340 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); 1341 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false; 1342 1343 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 1344 if (characteristic == null) return false; 1345 1346 BluetoothGattService service = characteristic.getService(); 1347 if (service == null) return false; 1348 1349 BluetoothDevice device = service.getDevice(); 1350 if (device == null) return false; 1351 1352 synchronized (mDeviceBusyLock) { 1353 if (mDeviceBusy) return false; 1354 mDeviceBusy = true; 1355 } 1356 1357 try { 1358 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), 1359 AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource); 1360 } catch (RemoteException e) { 1361 Log.e(TAG, "", e); 1362 mDeviceBusy = false; 1363 return false; 1364 } 1365 1366 return true; 1367 } 1368 1369 /** 1370 * Initiates a reliable write transaction for a given remote device. 1371 * 1372 * <p>Once a reliable write transaction has been initiated, all calls 1373 * to {@link #writeCharacteristic} are sent to the remote device for 1374 * verification and queued up for atomic execution. The application will 1375 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback 1376 * in response to every {@link #writeCharacteristic} call and is responsible 1377 * for verifying if the value has been transmitted accurately. 1378 * 1379 * <p>After all characteristics have been queued up and verified, 1380 * {@link #executeReliableWrite} will execute all writes. If a characteristic 1381 * was not written correctly, calling {@link #abortReliableWrite} will 1382 * cancel the current transaction without committing any values on the 1383 * remote device. 1384 * 1385 * @return true, if the reliable write transaction has been initiated 1386 */ 1387 @RequiresLegacyBluetoothPermission 1388 @RequiresBluetoothConnectPermission 1389 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) beginReliableWrite()1390 public boolean beginReliableWrite() { 1391 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); 1392 if (mService == null || mClientIf == 0) return false; 1393 1394 try { 1395 mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource); 1396 } catch (RemoteException e) { 1397 Log.e(TAG, "", e); 1398 return false; 1399 } 1400 1401 return true; 1402 } 1403 1404 /** 1405 * Executes a reliable write transaction for a given remote device. 1406 * 1407 * <p>This function will commit all queued up characteristic write 1408 * operations for a given remote device. 1409 * 1410 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is 1411 * invoked to indicate whether the transaction has been executed correctly. 1412 * 1413 * @return true, if the request to execute the transaction has been sent 1414 */ 1415 @RequiresLegacyBluetoothPermission 1416 @RequiresBluetoothConnectPermission 1417 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) executeReliableWrite()1418 public boolean executeReliableWrite() { 1419 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); 1420 if (mService == null || mClientIf == 0) return false; 1421 1422 synchronized (mDeviceBusyLock) { 1423 if (mDeviceBusy) return false; 1424 mDeviceBusy = true; 1425 } 1426 1427 try { 1428 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource); 1429 } catch (RemoteException e) { 1430 Log.e(TAG, "", e); 1431 mDeviceBusy = false; 1432 return false; 1433 } 1434 1435 return true; 1436 } 1437 1438 /** 1439 * Cancels a reliable write transaction for a given device. 1440 * 1441 * <p>Calling this function will discard all queued characteristic write 1442 * operations for a given remote device. 1443 */ 1444 @RequiresLegacyBluetoothPermission 1445 @RequiresBluetoothConnectPermission 1446 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) abortReliableWrite()1447 public void abortReliableWrite() { 1448 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); 1449 if (mService == null || mClientIf == 0) return; 1450 1451 try { 1452 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource); 1453 } catch (RemoteException e) { 1454 Log.e(TAG, "", e); 1455 } 1456 } 1457 1458 /** 1459 * @deprecated Use {@link #abortReliableWrite()} 1460 */ 1461 @Deprecated 1462 @RequiresBluetoothConnectPermission 1463 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) abortReliableWrite(BluetoothDevice mDevice)1464 public void abortReliableWrite(BluetoothDevice mDevice) { 1465 abortReliableWrite(); 1466 } 1467 1468 /** 1469 * Enable or disable notifications/indications for a given characteristic. 1470 * 1471 * <p>Once notifications are enabled for a characteristic, a 1472 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be 1473 * triggered if the remote device indicates that the given characteristic 1474 * has changed. 1475 * 1476 * @param characteristic The characteristic for which to enable notifications 1477 * @param enable Set to true to enable notifications/indications 1478 * @return true, if the requested notification status was set successfully 1479 */ 1480 @RequiresLegacyBluetoothPermission 1481 @RequiresBluetoothConnectPermission 1482 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable)1483 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, 1484 boolean enable) { 1485 if (DBG) { 1486 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() 1487 + " enable: " + enable); 1488 } 1489 if (mService == null || mClientIf == 0) return false; 1490 1491 BluetoothGattService service = characteristic.getService(); 1492 if (service == null) return false; 1493 1494 BluetoothDevice device = service.getDevice(); 1495 if (device == null) return false; 1496 1497 try { 1498 mService.registerForNotification(mClientIf, device.getAddress(), 1499 characteristic.getInstanceId(), enable, mAttributionSource); 1500 } catch (RemoteException e) { 1501 Log.e(TAG, "", e); 1502 return false; 1503 } 1504 1505 return true; 1506 } 1507 1508 /** 1509 * Clears the internal cache and forces a refresh of the services from the 1510 * remote device. 1511 * 1512 * @hide 1513 */ 1514 @UnsupportedAppUsage 1515 @RequiresBluetoothConnectPermission 1516 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) refresh()1517 public boolean refresh() { 1518 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); 1519 if (mService == null || mClientIf == 0) return false; 1520 1521 try { 1522 mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource); 1523 } catch (RemoteException e) { 1524 Log.e(TAG, "", e); 1525 return false; 1526 } 1527 1528 return true; 1529 } 1530 1531 /** 1532 * Read the RSSI for a connected remote device. 1533 * 1534 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be 1535 * invoked when the RSSI value has been read. 1536 * 1537 * @return true, if the RSSI value has been requested successfully 1538 */ 1539 @RequiresLegacyBluetoothPermission 1540 @RequiresBluetoothConnectPermission 1541 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readRemoteRssi()1542 public boolean readRemoteRssi() { 1543 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); 1544 if (mService == null || mClientIf == 0) return false; 1545 1546 try { 1547 mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource); 1548 } catch (RemoteException e) { 1549 Log.e(TAG, "", e); 1550 return false; 1551 } 1552 1553 return true; 1554 } 1555 1556 /** 1557 * Request an MTU size used for a given connection. 1558 * 1559 * <p>When performing a write request operation (write without response), 1560 * the data sent is truncated to the MTU size. This function may be used 1561 * to request a larger MTU size to be able to send more data at once. 1562 * 1563 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate 1564 * whether this operation was successful. 1565 * 1566 * @return true, if the new MTU value has been requested successfully 1567 */ 1568 @RequiresLegacyBluetoothPermission 1569 @RequiresBluetoothConnectPermission 1570 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestMtu(int mtu)1571 public boolean requestMtu(int mtu) { 1572 if (DBG) { 1573 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() 1574 + " mtu: " + mtu); 1575 } 1576 if (mService == null || mClientIf == 0) return false; 1577 1578 try { 1579 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource); 1580 } catch (RemoteException e) { 1581 Log.e(TAG, "", e); 1582 return false; 1583 } 1584 1585 return true; 1586 } 1587 1588 /** 1589 * Request a connection parameter update. 1590 * 1591 * <p>This function will send a connection parameter update request to the 1592 * remote device. 1593 * 1594 * @param connectionPriority Request a specific connection priority. Must be one of {@link 1595 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} 1596 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. 1597 * @throws IllegalArgumentException If the parameters are outside of their specified range. 1598 */ 1599 @RequiresBluetoothConnectPermission 1600 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestConnectionPriority(int connectionPriority)1601 public boolean requestConnectionPriority(int connectionPriority) { 1602 if (connectionPriority < CONNECTION_PRIORITY_BALANCED 1603 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { 1604 throw new IllegalArgumentException("connectionPriority not within valid range"); 1605 } 1606 1607 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority); 1608 if (mService == null || mClientIf == 0) return false; 1609 1610 try { 1611 mService.connectionParameterUpdate( 1612 mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); 1613 } catch (RemoteException e) { 1614 Log.e(TAG, "", e); 1615 return false; 1616 } 1617 1618 return true; 1619 } 1620 1621 /** 1622 * Request an LE connection parameter update. 1623 * 1624 * <p>This function will send an LE connection parameters update request to the remote device. 1625 * 1626 * @return true, if the request is send to the Bluetooth stack. 1627 * @hide 1628 */ 1629 @RequiresBluetoothConnectPermission 1630 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, int slaveLatency, int supervisionTimeout, int minConnectionEventLen, int maxConnectionEventLen)1631 public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, 1632 int slaveLatency, int supervisionTimeout, 1633 int minConnectionEventLen, int maxConnectionEventLen) { 1634 if (DBG) { 1635 Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval 1636 + ")" + (1.25 * minConnectionInterval) 1637 + "msec, max=(" + maxConnectionInterval + ")" 1638 + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency 1639 + ", timeout=" + supervisionTimeout + "msec" + ", min_ce=" 1640 + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen); 1641 } 1642 if (mService == null || mClientIf == 0) return false; 1643 1644 try { 1645 mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), 1646 minConnectionInterval, maxConnectionInterval, 1647 slaveLatency, supervisionTimeout, 1648 minConnectionEventLen, maxConnectionEventLen, 1649 mAttributionSource); 1650 } catch (RemoteException e) { 1651 Log.e(TAG, "", e); 1652 return false; 1653 } 1654 1655 return true; 1656 } 1657 1658 /** 1659 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1660 * with {@link BluetoothProfile#GATT} as argument 1661 * 1662 * @throws UnsupportedOperationException 1663 */ 1664 @Override 1665 @RequiresNoPermission getConnectionState(BluetoothDevice device)1666 public int getConnectionState(BluetoothDevice device) { 1667 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 1668 } 1669 1670 /** 1671 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 1672 * with {@link BluetoothProfile#GATT} as argument 1673 * 1674 * @throws UnsupportedOperationException 1675 */ 1676 @Override 1677 @RequiresNoPermission getConnectedDevices()1678 public List<BluetoothDevice> getConnectedDevices() { 1679 throw new UnsupportedOperationException( 1680 "Use BluetoothManager#getConnectedDevices instead."); 1681 } 1682 1683 /** 1684 * Not supported - please use 1685 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 1686 * with {@link BluetoothProfile#GATT} as first argument 1687 * 1688 * @throws UnsupportedOperationException 1689 */ 1690 @Override 1691 @RequiresNoPermission getDevicesMatchingConnectionStates(int[] states)1692 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1693 throw new UnsupportedOperationException( 1694 "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 1695 } 1696 } 1697