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