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