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