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