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