1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; 21 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; 22 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.RequiresNoPermission; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SuppressLint; 28 import android.bluetooth.annotations.RequiresBluetoothConnectPermission; 29 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; 30 import android.content.AttributionSource; 31 import android.os.IBinder; 32 import android.os.ParcelUuid; 33 import android.os.RemoteException; 34 import android.util.Log; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.UUID; 41 42 /** 43 * Public API for the Bluetooth GATT Profile server role. 44 * 45 * <p>This class provides Bluetooth GATT server role functionality, allowing applications to create 46 * Bluetooth Smart services and characteristics. 47 * 48 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service via IPC. Use 49 * {@link BluetoothManager#openGattServer} to get an instance of this class. 50 */ 51 public final class BluetoothGattServer implements BluetoothProfile { 52 private static final String TAG = BluetoothGattServer.class.getSimpleName(); 53 54 private static final boolean DBG = true; 55 private static final boolean VDBG = false; 56 57 private final IBluetoothGatt mService; 58 private final BluetoothAdapter mAdapter; 59 private final AttributionSource mAttributionSource; 60 61 private BluetoothGattServerCallback mCallback; 62 63 private final Object mServerIfLock = new Object(); 64 private int mServerIf; 65 private final int mTransport; 66 private BluetoothGattService mPendingService; 67 private final List<BluetoothGattService> mServices; 68 69 private static final int CALLBACK_REG_TIMEOUT = 10000; 70 // Max length of an attribute value, defined in gatt_api.h 71 private static final int GATT_MAX_ATTR_LEN = 512; 72 73 /** Bluetooth GATT interface callbacks */ 74 @SuppressLint("AndroidFrameworkBluetoothPermission") 75 private final IBluetoothGattServerCallback mBluetoothGattServerCallback = 76 new IBluetoothGattServerCallback.Stub() { 77 /** 78 * Application interface registered - app is ready to go 79 * 80 * @hide 81 */ 82 @Override 83 public void onServerRegistered(int status, int serverIf) { 84 if (DBG) { 85 Log.d( 86 TAG, 87 "onServerRegistered() - status=" 88 + status 89 + " serverIf=" 90 + serverIf); 91 } 92 synchronized (mServerIfLock) { 93 if (mCallback != null) { 94 mServerIf = serverIf; 95 mServerIfLock.notify(); 96 } else { 97 // registration timeout 98 Log.e(TAG, "onServerRegistered: mCallback is null"); 99 } 100 } 101 } 102 103 /** 104 * Server connection state changed 105 * 106 * @hide 107 */ 108 @Override 109 public void onServerConnectionState( 110 int status, int serverIf, boolean connected, String address) { 111 if (DBG) { 112 Log.d( 113 TAG, 114 "onServerConnectionState() - status=" 115 + status 116 + " serverIf=" 117 + serverIf 118 + " connected=" 119 + connected 120 + " device=" 121 + BluetoothUtils.toAnonymizedAddress(address)); 122 } 123 try { 124 mCallback.onConnectionStateChange( 125 mAdapter.getRemoteDevice(address), 126 status, 127 connected ? STATE_CONNECTED : STATE_DISCONNECTED); 128 } catch (Exception ex) { 129 Log.w(TAG, "Unhandled exception in callback", ex); 130 } 131 } 132 133 /** 134 * Service has been added 135 * 136 * @hide 137 */ 138 @Override 139 public void onServiceAdded(int status, BluetoothGattService service) { 140 if (DBG) { 141 Log.d( 142 TAG, 143 "onServiceAdded() - handle=" 144 + service.getInstanceId() 145 + " uuid=" 146 + service.getUuid() 147 + " status=" 148 + status); 149 } 150 151 if (mPendingService == null) { 152 return; 153 } 154 155 BluetoothGattService tmp = mPendingService; 156 mPendingService = null; 157 158 // Rewrite newly assigned handles to existing service. 159 tmp.setInstanceId(service.getInstanceId()); 160 List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics(); 161 List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics(); 162 for (int i = 0; i < svc_chars.size(); i++) { 163 BluetoothGattCharacteristic temp_char = temp_chars.get(i); 164 BluetoothGattCharacteristic svc_char = svc_chars.get(i); 165 166 temp_char.setInstanceId(svc_char.getInstanceId()); 167 168 List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors(); 169 List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors(); 170 for (int j = 0; j < svc_descs.size(); j++) { 171 temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId()); 172 } 173 } 174 175 mServices.add(tmp); 176 177 try { 178 mCallback.onServiceAdded((int) status, tmp); 179 } catch (Exception ex) { 180 Log.w(TAG, "Unhandled exception in callback", ex); 181 } 182 } 183 184 /** 185 * Remote client characteristic read request. 186 * 187 * @hide 188 */ 189 @Override 190 public void onCharacteristicReadRequest( 191 String address, int transId, int offset, boolean isLong, int handle) { 192 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); 193 194 BluetoothDevice device = mAdapter.getRemoteDevice(address); 195 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); 196 if (characteristic == null) { 197 Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle); 198 return; 199 } 200 201 try { 202 mCallback.onCharacteristicReadRequest( 203 device, transId, offset, characteristic); 204 } catch (Exception ex) { 205 Log.w(TAG, "Unhandled exception in callback", ex); 206 } 207 } 208 209 /** 210 * Remote client descriptor read request. 211 * 212 * @hide 213 */ 214 @Override 215 public void onDescriptorReadRequest( 216 String address, int transId, int offset, boolean isLong, int handle) { 217 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); 218 219 BluetoothDevice device = mAdapter.getRemoteDevice(address); 220 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); 221 if (descriptor == null) { 222 Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle); 223 return; 224 } 225 226 try { 227 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); 228 } catch (Exception ex) { 229 Log.w(TAG, "Unhandled exception in callback", ex); 230 } 231 } 232 233 /** 234 * Remote client characteristic write request. 235 * 236 * @hide 237 */ 238 @Override 239 public void onCharacteristicWriteRequest( 240 String address, 241 int transId, 242 int offset, 243 int length, 244 boolean isPrep, 245 boolean needRsp, 246 int handle, 247 byte[] value) { 248 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle); 249 250 BluetoothDevice device = mAdapter.getRemoteDevice(address); 251 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); 252 if (characteristic == null) { 253 Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle); 254 return; 255 } 256 257 try { 258 mCallback.onCharacteristicWriteRequest( 259 device, transId, characteristic, isPrep, needRsp, offset, value); 260 } catch (Exception ex) { 261 Log.w(TAG, "Unhandled exception in callback", ex); 262 } 263 } 264 265 /** 266 * Remote client descriptor write request. 267 * 268 * @hide 269 */ 270 @Override 271 public void onDescriptorWriteRequest( 272 String address, 273 int transId, 274 int offset, 275 int length, 276 boolean isPrep, 277 boolean needRsp, 278 int handle, 279 byte[] value) { 280 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle); 281 282 BluetoothDevice device = mAdapter.getRemoteDevice(address); 283 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); 284 if (descriptor == null) { 285 Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle); 286 return; 287 } 288 289 try { 290 mCallback.onDescriptorWriteRequest( 291 device, transId, descriptor, isPrep, needRsp, offset, value); 292 } catch (Exception ex) { 293 Log.w(TAG, "Unhandled exception in callback", ex); 294 } 295 } 296 297 /** 298 * Execute pending writes. 299 * 300 * @hide 301 */ 302 @Override 303 public void onExecuteWrite(String address, int transId, boolean execWrite) { 304 if (DBG) { 305 Log.d( 306 TAG, 307 "onExecuteWrite() - " 308 + "device=" 309 + BluetoothUtils.toAnonymizedAddress(address) 310 + ", transId=" 311 + transId 312 + "execWrite=" 313 + execWrite); 314 } 315 316 BluetoothDevice device = mAdapter.getRemoteDevice(address); 317 if (device == null) return; 318 319 try { 320 mCallback.onExecuteWrite(device, transId, execWrite); 321 } catch (Exception ex) { 322 Log.w(TAG, "Unhandled exception in callback", ex); 323 } 324 } 325 326 /** 327 * A notification/indication has been sent. 328 * 329 * @hide 330 */ 331 @Override 332 public void onNotificationSent(String address, int status) { 333 if (VDBG) { 334 Log.d( 335 TAG, 336 "onNotificationSent() - " 337 + "device=" 338 + BluetoothUtils.toAnonymizedAddress(address) 339 + ", status=" 340 + status); 341 } 342 343 BluetoothDevice device = mAdapter.getRemoteDevice(address); 344 if (device == null) return; 345 346 try { 347 mCallback.onNotificationSent(device, status); 348 } catch (Exception ex) { 349 Log.w(TAG, "Unhandled exception: " + ex); 350 } 351 } 352 353 /** 354 * The MTU for a connection has changed 355 * 356 * @hide 357 */ 358 @Override 359 public void onMtuChanged(String address, int mtu) { 360 if (DBG) { 361 Log.d( 362 TAG, 363 "onMtuChanged() - " 364 + "device=" 365 + BluetoothUtils.toAnonymizedAddress(address) 366 + ", mtu=" 367 + mtu); 368 } 369 370 BluetoothDevice device = mAdapter.getRemoteDevice(address); 371 if (device == null) return; 372 373 try { 374 mCallback.onMtuChanged(device, mtu); 375 } catch (Exception ex) { 376 Log.w(TAG, "Unhandled exception: " + ex); 377 } 378 } 379 380 /** 381 * The PHY for a connection was updated 382 * 383 * @hide 384 */ 385 @Override 386 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { 387 if (DBG) { 388 Log.d( 389 TAG, 390 "onPhyUpdate() - " 391 + "device=" 392 + BluetoothUtils.toAnonymizedAddress(address) 393 + ", txPHy=" 394 + txPhy 395 + ", rxPHy=" 396 + rxPhy); 397 } 398 399 BluetoothDevice device = mAdapter.getRemoteDevice(address); 400 if (device == null) return; 401 402 try { 403 mCallback.onPhyUpdate(device, txPhy, rxPhy, status); 404 } catch (Exception ex) { 405 Log.w(TAG, "Unhandled exception: " + ex); 406 } 407 } 408 409 /** 410 * The PHY for a connection was read 411 * 412 * @hide 413 */ 414 @Override 415 public void onPhyRead(String address, int txPhy, int rxPhy, int status) { 416 if (DBG) { 417 Log.d( 418 TAG, 419 "onPhyUpdate() - " 420 + "device=" 421 + BluetoothUtils.toAnonymizedAddress(address) 422 + ", txPHy=" 423 + txPhy 424 + ", rxPHy=" 425 + rxPhy); 426 } 427 428 BluetoothDevice device = mAdapter.getRemoteDevice(address); 429 if (device == null) return; 430 431 try { 432 mCallback.onPhyRead(device, txPhy, rxPhy, status); 433 } catch (Exception ex) { 434 Log.w(TAG, "Unhandled exception: " + ex); 435 } 436 } 437 438 /** 439 * Callback invoked when the given connection is updated 440 * 441 * @hide 442 */ 443 @Override 444 public void onConnectionUpdated( 445 String address, int interval, int latency, int timeout, int status) { 446 if (DBG) { 447 Log.d( 448 TAG, 449 "onConnectionUpdated() - Device=" 450 + BluetoothUtils.toAnonymizedAddress(address) 451 + " interval=" 452 + interval 453 + " latency=" 454 + latency 455 + " timeout=" 456 + timeout 457 + " status=" 458 + status); 459 } 460 BluetoothDevice device = mAdapter.getRemoteDevice(address); 461 if (device == null) return; 462 463 try { 464 mCallback.onConnectionUpdated(device, interval, latency, timeout, status); 465 } catch (Exception ex) { 466 Log.w(TAG, "Unhandled exception: " + ex); 467 } 468 } 469 470 /** 471 * Callback invoked when the given connection's subrate parameters are changed 472 * 473 * @hide 474 */ 475 @Override 476 public void onSubrateChange( 477 String address, 478 int subrateFactor, 479 int latency, 480 int contNum, 481 int timeout, 482 int status) { 483 if (DBG) { 484 Log.d( 485 TAG, 486 "onSubrateChange() - " 487 + "Device=" 488 + BluetoothUtils.toAnonymizedAddress(address) 489 + ", subrateFactor=" 490 + subrateFactor 491 + ", latency=" 492 + latency 493 + ", contNum=" 494 + contNum 495 + ", timeout=" 496 + timeout 497 + ", status=" 498 + status); 499 } 500 BluetoothDevice device = mAdapter.getRemoteDevice(address); 501 if (device == null) { 502 return; 503 } 504 505 try { 506 mCallback.onSubrateChange( 507 device, subrateFactor, latency, contNum, timeout, status); 508 } catch (Exception ex) { 509 Log.w(TAG, "Unhandled exception: " + ex); 510 } 511 } 512 }; 513 514 /** Create a BluetoothGattServer proxy object. */ BluetoothGattServer( IBluetoothGatt iGatt, int transport, BluetoothAdapter adapter)515 /* package */ BluetoothGattServer( 516 IBluetoothGatt iGatt, int transport, BluetoothAdapter adapter) { 517 mService = iGatt; 518 mAdapter = adapter; 519 mAttributionSource = adapter.getAttributionSource(); 520 mCallback = null; 521 mServerIf = 0; 522 mTransport = transport; 523 mServices = new ArrayList<BluetoothGattService>(); 524 } 525 526 /** 527 * Get the identifier of the BluetoothGattServer, or 0 if it is closed 528 * 529 * @hide 530 */ 531 @RequiresNoPermission getServerIf()532 public int getServerIf() { 533 return mServerIf; 534 } 535 536 /** 537 * Returns a characteristic with given handle. 538 * 539 * @hide 540 */ getCharacteristicByHandle(int handle)541 /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) { 542 for (BluetoothGattService svc : mServices) { 543 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 544 if (charac.getInstanceId() == handle) { 545 return charac; 546 } 547 } 548 } 549 return null; 550 } 551 552 /** 553 * Returns a descriptor with given handle. 554 * 555 * @hide 556 */ getDescriptorByHandle(int handle)557 /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) { 558 for (BluetoothGattService svc : mServices) { 559 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 560 for (BluetoothGattDescriptor desc : charac.getDescriptors()) { 561 if (desc.getInstanceId() == handle) { 562 return desc; 563 } 564 } 565 } 566 } 567 return null; 568 } 569 570 /** @hide */ 571 @Override 572 @RequiresNoPermission onServiceConnected(IBinder service)573 public void onServiceConnected(IBinder service) {} 574 575 /** @hide */ 576 @Override 577 @RequiresNoPermission onServiceDisconnected()578 public void onServiceDisconnected() {} 579 580 /** @hide */ 581 @Override 582 @RequiresNoPermission getAdapter()583 public BluetoothAdapter getAdapter() { 584 return mAdapter; 585 } 586 587 /** 588 * Close this GATT server instance. 589 * 590 * <p>Application should call this method as early as possible after it is done with this GATT 591 * server. 592 */ 593 @RequiresBluetoothConnectPermission 594 @RequiresPermission(BLUETOOTH_CONNECT) close()595 public void close() { 596 if (DBG) Log.d(TAG, "close()"); 597 unregisterCallback(); 598 } 599 600 /** 601 * Register an application callback to start using GattServer. 602 * 603 * <p>This is an asynchronous call. The callback is used to notify success or failure if the 604 * function returns true. 605 * 606 * @param callback GATT callback handler that will receive asynchronous callbacks. 607 * @return true, the callback will be called to notify success or failure, false on immediate 608 * error 609 */ 610 @RequiresLegacyBluetoothPermission 611 @RequiresBluetoothConnectPermission 612 @RequiresPermission(BLUETOOTH_CONNECT) registerCallback(BluetoothGattServerCallback callback)613 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { 614 return registerCallback(callback, false); 615 } 616 617 /** 618 * Register an application callback to start using GattServer. 619 * 620 * <p>This is an asynchronous call. The callback is used to notify success or failure if the 621 * function returns true. 622 * 623 * @param callback GATT callback handler that will receive asynchronous callbacks. 624 * @param eattSupport indicates if server can use eatt 625 * @return true, the callback will be called to notify success or failure, false on immediate 626 * error 627 * @hide 628 */ 629 @RequiresLegacyBluetoothPermission 630 @RequiresBluetoothConnectPermission 631 @RequiresPermission(BLUETOOTH_CONNECT) 632 @SuppressWarnings("WaitNotInLoop") // TODO(b/314811467) registerCallback( BluetoothGattServerCallback callback, boolean eattSupport)633 /*package*/ boolean registerCallback( 634 BluetoothGattServerCallback callback, boolean eattSupport) { 635 if (DBG) Log.d(TAG, "registerCallback()"); 636 if (mService == null) { 637 Log.e(TAG, "GATT service not available"); 638 return false; 639 } 640 UUID uuid = UUID.randomUUID(); 641 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); 642 643 synchronized (mServerIfLock) { 644 if (mCallback != null) { 645 Log.e(TAG, "App can register callback only once"); 646 return false; 647 } 648 649 mCallback = callback; 650 try { 651 mService.registerServer( 652 new ParcelUuid(uuid), 653 mBluetoothGattServerCallback, 654 eattSupport, 655 mAttributionSource); 656 } catch (RemoteException e) { 657 Log.e(TAG, "", e); 658 mCallback = null; 659 return false; 660 } 661 662 try { 663 mServerIfLock.wait(CALLBACK_REG_TIMEOUT); 664 } catch (InterruptedException e) { 665 Log.e(TAG, "" + e); 666 mCallback = null; 667 } 668 669 if (mServerIf == 0) { 670 mCallback = null; 671 return false; 672 } else { 673 return true; 674 } 675 } 676 } 677 678 /** Unregister the current application and callbacks. */ 679 @RequiresBluetoothConnectPermission 680 @RequiresPermission(BLUETOOTH_CONNECT) unregisterCallback()681 private void unregisterCallback() { 682 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); 683 if (mService == null || mServerIf == 0) return; 684 685 try { 686 mCallback = null; 687 mService.unregisterServer(mServerIf, mAttributionSource); 688 mServerIf = 0; 689 } catch (RemoteException e) { 690 Log.e(TAG, "", e); 691 } 692 } 693 694 /** 695 * Returns a service by UUID, instance and type. 696 * 697 * @hide 698 */ getService(UUID uuid, int instanceId, int type)699 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { 700 for (BluetoothGattService svc : mServices) { 701 if (svc.getType() == type 702 && svc.getInstanceId() == instanceId 703 && svc.getUuid().equals(uuid)) { 704 return svc; 705 } 706 } 707 return null; 708 } 709 710 /** 711 * Initiate a connection to a Bluetooth GATT capable device. 712 * 713 * <p>The connection may not be established right away, but will be completed when the remote 714 * device is available. A {@link BluetoothGattServerCallback#onConnectionStateChange} callback 715 * will be invoked when the connection state changes as a result of this function. 716 * 717 * <p>The autoConnect parameter determines whether to actively connect to the remote device, or 718 * rather passively scan and finalize the connection when the remote device is in 719 * range/available. Generally, the first ever connection to a device should be direct 720 * (autoConnect set to false) and subsequent connections to known devices should be invoked with 721 * the autoConnect parameter set to true. 722 * 723 * @param autoConnect Whether to directly connect to the remote device (false) or to 724 * automatically connect as soon as the remote device becomes available (true). 725 * @return true, if the connection attempt was initiated successfully 726 */ 727 @RequiresLegacyBluetoothPermission 728 @RequiresBluetoothConnectPermission 729 @RequiresPermission(BLUETOOTH_CONNECT) connect(BluetoothDevice device, boolean autoConnect)730 public boolean connect(BluetoothDevice device, boolean autoConnect) { 731 if (DBG) { 732 Log.d(TAG, "connect() - device: " + device + ", auto: " + autoConnect); 733 } 734 if (mService == null || mServerIf == 0) return false; 735 736 try { 737 // autoConnect is inverse of "isDirect" 738 mService.serverConnect( 739 mServerIf, 740 device.getAddress(), 741 device.getAddressType(), 742 !autoConnect, 743 mTransport, 744 mAttributionSource); 745 } catch (RemoteException e) { 746 Log.e(TAG, "", e); 747 return false; 748 } 749 750 return true; 751 } 752 753 /** 754 * Disconnects an established connection, or cancels a connection attempt currently in progress. 755 * 756 * @param device Remote device 757 */ 758 @RequiresLegacyBluetoothPermission 759 @RequiresBluetoothConnectPermission 760 @RequiresPermission(BLUETOOTH_CONNECT) cancelConnection(BluetoothDevice device)761 public void cancelConnection(BluetoothDevice device) { 762 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device); 763 if (mService == null || mServerIf == 0) return; 764 765 try { 766 mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource); 767 } catch (RemoteException e) { 768 Log.e(TAG, "", e); 769 } 770 } 771 772 /** 773 * Set the preferred connection PHY for this app. Please note that this is just a 774 * recommendation, whether the PHY change will happen depends on other applications preferences, 775 * local and remote controller capabilities. Controller can override these settings. 776 * 777 * <p>{@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this 778 * call, even if no PHY change happens. It is also triggered when remote device updates the PHY. 779 * 780 * @param device The remote device to send this response to 781 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link 782 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 783 * BluetoothDevice#PHY_LE_CODED_MASK}. 784 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link 785 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 786 * BluetoothDevice#PHY_LE_CODED_MASK}. 787 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one 788 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} 789 * or {@link BluetoothDevice#PHY_OPTION_S8} 790 */ 791 @RequiresBluetoothConnectPermission 792 @RequiresPermission(BLUETOOTH_CONNECT) setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions)793 public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { 794 try { 795 mService.serverSetPreferredPhy( 796 mServerIf, device.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource); 797 } catch (RemoteException e) { 798 Log.e(TAG, "", e); 799 } 800 } 801 802 /** 803 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned 804 * in {@link BluetoothGattServerCallback#onPhyRead} 805 * 806 * @param device The remote device to send this response to 807 */ 808 @RequiresBluetoothConnectPermission 809 @RequiresPermission(BLUETOOTH_CONNECT) readPhy(BluetoothDevice device)810 public void readPhy(BluetoothDevice device) { 811 try { 812 mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource); 813 } catch (RemoteException e) { 814 Log.e(TAG, "", e); 815 } 816 } 817 818 /** 819 * Send a response to a read or write request to a remote device. 820 * 821 * <p>This function must be invoked in when a remote read/write request is received by one of 822 * these callback methods: 823 * 824 * <ul> 825 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} 826 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest} 827 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest} 828 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest} 829 * </ul> 830 * 831 * @param device The remote device to send this response to 832 * @param requestId The ID of the request that was received with the callback 833 * @param status The status of the request to be sent to the remote devices 834 * @param offset Value offset for partial read/write response 835 * @param value The value of the attribute that was read/written (optional) 836 */ 837 @RequiresLegacyBluetoothPermission 838 @RequiresBluetoothConnectPermission 839 @RequiresPermission(BLUETOOTH_CONNECT) sendResponse( BluetoothDevice device, int requestId, int status, int offset, byte[] value)840 public boolean sendResponse( 841 BluetoothDevice device, int requestId, int status, int offset, byte[] value) { 842 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device); 843 if (mService == null || mServerIf == 0) return false; 844 845 try { 846 mService.sendResponse( 847 mServerIf, 848 device.getAddress(), 849 requestId, 850 status, 851 offset, 852 value, 853 mAttributionSource); 854 } catch (RemoteException e) { 855 Log.e(TAG, "", e); 856 return false; 857 } 858 return true; 859 } 860 861 /** 862 * Send a notification or indication that a local characteristic has been updated. 863 * 864 * <p>A notification or indication is sent to the remote device to signal that the 865 * characteristic has been updated. This function should be invoked for every client that 866 * requests notifications/indications by writing to the "Client Configuration" descriptor for 867 * the given characteristic. 868 * 869 * @param device The remote device to receive the notification/indication 870 * @param characteristic The local characteristic that has been updated 871 * @param confirm true to request confirmation from the client (indication), false to send a 872 * notification 873 * @return true, if the notification has been triggered successfully 874 * @throws IllegalArgumentException if the characteristic value or service is null 875 * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice, 876 * BluetoothGattCharacteristic, boolean, byte[])} as this is not memory safe. 877 */ 878 @Deprecated 879 @RequiresLegacyBluetoothPermission 880 @RequiresBluetoothConnectPermission 881 @RequiresPermission(BLUETOOTH_CONNECT) notifyCharacteristicChanged( BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm)882 public boolean notifyCharacteristicChanged( 883 BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { 884 return notifyCharacteristicChanged( 885 device, characteristic, confirm, characteristic.getValue()) 886 == BluetoothStatusCodes.SUCCESS; 887 } 888 889 /** @hide */ 890 @Retention(RetentionPolicy.SOURCE) 891 @IntDef( 892 value = { 893 BluetoothStatusCodes.SUCCESS, 894 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 895 BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, 896 BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, 897 BluetoothStatusCodes.ERROR_UNKNOWN 898 }) 899 public @interface NotifyCharacteristicReturnValues {} 900 901 /** 902 * Send a notification or indication that a local characteristic has been updated. 903 * 904 * <p>A notification or indication is sent to the remote device to signal that the 905 * characteristic has been updated. This function should be invoked for every client that 906 * requests notifications/indications by writing to the "Client Configuration" descriptor for 907 * the given characteristic. 908 * 909 * @param device the remote device to receive the notification/indication 910 * @param characteristic the local characteristic that has been updated 911 * @param confirm {@code true} to request confirmation from the client (indication) or {@code 912 * false} to send a notification 913 * @param value the characteristic value 914 * @return whether the notification has been triggered successfully 915 * @throws IllegalArgumentException if the characteristic value or service is null 916 */ 917 @RequiresLegacyBluetoothPermission 918 @RequiresBluetoothConnectPermission 919 @RequiresPermission(BLUETOOTH_CONNECT) 920 @NotifyCharacteristicReturnValues notifyCharacteristicChanged( @onNull BluetoothDevice device, @NonNull BluetoothGattCharacteristic characteristic, boolean confirm, @NonNull byte[] value)921 public int notifyCharacteristicChanged( 922 @NonNull BluetoothDevice device, 923 @NonNull BluetoothGattCharacteristic characteristic, 924 boolean confirm, 925 @NonNull byte[] value) { 926 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device); 927 if (mService == null || mServerIf == 0) { 928 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; 929 } 930 931 if (characteristic == null) { 932 throw new IllegalArgumentException("characteristic must not be null"); 933 } 934 if (device == null) { 935 throw new IllegalArgumentException("device must not be null"); 936 } 937 if (value.length > GATT_MAX_ATTR_LEN) { 938 throw new IllegalArgumentException( 939 "notification should not be longer than max length of an attribute value"); 940 } 941 BluetoothGattService service = characteristic.getService(); 942 if (service == null) { 943 throw new IllegalArgumentException("Characteristic must have a non-null service"); 944 } 945 if (value == null) { 946 throw new IllegalArgumentException("Characteristic value must not be null"); 947 } 948 949 try { 950 return mService.sendNotification( 951 mServerIf, 952 device.getAddress(), 953 characteristic.getInstanceId(), 954 confirm, 955 value, 956 mAttributionSource); 957 } catch (RemoteException e) { 958 Log.e(TAG, "", e); 959 throw e.rethrowAsRuntimeException(); 960 } 961 } 962 963 /** 964 * Add a service to the list of services to be hosted. 965 * 966 * <p>Once a service has been added to the list, the service and its included characteristics 967 * will be provided by the local device. 968 * 969 * <p>If the local device has already exposed services when this function is called, a service 970 * update notification will be sent to all clients. 971 * 972 * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate whether this 973 * service has been added successfully. Do not add another service before this callback. 974 * 975 * @param service Service to be added to the list of services provided by this device. 976 * @return true, if the request to add service has been initiated 977 */ 978 @RequiresLegacyBluetoothPermission 979 @RequiresBluetoothConnectPermission 980 @RequiresPermission(BLUETOOTH_CONNECT) addService(BluetoothGattService service)981 public boolean addService(BluetoothGattService service) { 982 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); 983 if (mService == null || mServerIf == 0) return false; 984 985 mPendingService = service; 986 987 try { 988 mService.addService(mServerIf, service, mAttributionSource); 989 } catch (RemoteException e) { 990 Log.e(TAG, "", e); 991 return false; 992 } 993 994 return true; 995 } 996 997 /** 998 * Removes a service from the list of services to be provided. 999 * 1000 * @param service Service to be removed. 1001 * @return true, if the service has been removed 1002 */ 1003 @RequiresLegacyBluetoothPermission 1004 @RequiresBluetoothConnectPermission 1005 @RequiresPermission(BLUETOOTH_CONNECT) removeService(BluetoothGattService service)1006 public boolean removeService(BluetoothGattService service) { 1007 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); 1008 if (mService == null || mServerIf == 0) return false; 1009 1010 BluetoothGattService intService = 1011 getService(service.getUuid(), service.getInstanceId(), service.getType()); 1012 if (intService == null) return false; 1013 1014 try { 1015 mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource); 1016 mServices.remove(intService); 1017 } catch (RemoteException e) { 1018 Log.e(TAG, "", e); 1019 return false; 1020 } 1021 1022 return true; 1023 } 1024 1025 /** Remove all services from the list of provided services. */ 1026 @RequiresLegacyBluetoothPermission 1027 @RequiresBluetoothConnectPermission 1028 @RequiresPermission(BLUETOOTH_CONNECT) clearServices()1029 public void clearServices() { 1030 if (DBG) Log.d(TAG, "clearServices()"); 1031 if (mService == null || mServerIf == 0) return; 1032 1033 try { 1034 mService.clearServices(mServerIf, mAttributionSource); 1035 mServices.clear(); 1036 } catch (RemoteException e) { 1037 Log.e(TAG, "", e); 1038 } 1039 } 1040 1041 /** 1042 * Returns a list of GATT services offered by this device. 1043 * 1044 * <p>An application must call {@link #addService} to add a service to the list of services 1045 * offered by this device. 1046 * 1047 * @return List of services. Returns an empty list if no services have been added yet. 1048 */ 1049 @RequiresLegacyBluetoothPermission 1050 @RequiresNoPermission getServices()1051 public List<BluetoothGattService> getServices() { 1052 return mServices; 1053 } 1054 1055 /** 1056 * Returns a {@link BluetoothGattService} from the list of services offered by this device. 1057 * 1058 * <p>If multiple instances of the same service (as identified by UUID) exist, the first 1059 * instance of the service is returned. 1060 * 1061 * @param uuid UUID of the requested service 1062 * @return BluetoothGattService if supported, or null if the requested service is not offered by 1063 * this device. 1064 */ 1065 @RequiresLegacyBluetoothPermission 1066 @RequiresNoPermission getService(UUID uuid)1067 public BluetoothGattService getService(UUID uuid) { 1068 for (BluetoothGattService service : mServices) { 1069 if (service.getUuid().equals(uuid)) { 1070 return service; 1071 } 1072 } 1073 1074 return null; 1075 } 1076 1077 /** 1078 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with {@link 1079 * BluetoothProfile#GATT} as argument 1080 * 1081 * @throws UnsupportedOperationException on every call 1082 */ 1083 @Override 1084 @RequiresNoPermission getConnectionState(BluetoothDevice device)1085 public int getConnectionState(BluetoothDevice device) { 1086 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 1087 } 1088 1089 /** 1090 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with {@link 1091 * BluetoothProfile#GATT} as argument 1092 * 1093 * @throws UnsupportedOperationException on every call 1094 */ 1095 @Override 1096 @RequiresNoPermission getConnectedDevices()1097 public List<BluetoothDevice> getConnectedDevices() { 1098 throw new UnsupportedOperationException( 1099 "Use BluetoothManager#getConnectedDevices instead."); 1100 } 1101 1102 /** 1103 * Not supported - please use {@link BluetoothManager#getDevicesMatchingConnectionStates(int, 1104 * int[])} with {@link BluetoothProfile#GATT} as first argument 1105 * 1106 * @throws UnsupportedOperationException on every call 1107 */ 1108 @Override 1109 @RequiresNoPermission getDevicesMatchingConnectionStates(int[] states)1110 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1111 throw new UnsupportedOperationException( 1112 "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 1113 } 1114 } 1115