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