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.ParcelUuid; 20 import android.os.RemoteException; 21 import android.util.Log; 22 23 import java.util.ArrayList; 24 import java.util.List; 25 import java.util.UUID; 26 27 /** 28 * Public API for the Bluetooth GATT Profile server role. 29 * 30 * <p>This class provides Bluetooth GATT server role functionality, 31 * allowing applications to create Bluetooth Smart services and 32 * characteristics. 33 * 34 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service 35 * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance 36 * of this class. 37 */ 38 public final class BluetoothGattServer implements BluetoothProfile { 39 private static final String TAG = "BluetoothGattServer"; 40 private static final boolean DBG = true; 41 private static final boolean VDBG = false; 42 43 private BluetoothAdapter mAdapter; 44 private IBluetoothGatt mService; 45 private BluetoothGattServerCallback mCallback; 46 47 private Object mServerIfLock = new Object(); 48 private int mServerIf; 49 private int mTransport; 50 private BluetoothGattService mPendingService; 51 private List<BluetoothGattService> mServices; 52 53 private static final int CALLBACK_REG_TIMEOUT = 10000; 54 55 /** 56 * Bluetooth GATT interface callbacks 57 */ 58 private final IBluetoothGattServerCallback mBluetoothGattServerCallback = 59 new IBluetoothGattServerCallback.Stub() { 60 /** 61 * Application interface registered - app is ready to go 62 * @hide 63 */ 64 @Override 65 public void onServerRegistered(int status, int serverIf) { 66 if (DBG) { 67 Log.d(TAG, "onServerRegistered() - status=" + status 68 + " serverIf=" + serverIf); 69 } 70 synchronized (mServerIfLock) { 71 if (mCallback != null) { 72 mServerIf = serverIf; 73 mServerIfLock.notify(); 74 } else { 75 // registration timeout 76 Log.e(TAG, "onServerRegistered: mCallback is null"); 77 } 78 } 79 } 80 81 /** 82 * Server connection state changed 83 * @hide 84 */ 85 @Override 86 public void onServerConnectionState(int status, int serverIf, 87 boolean connected, String address) { 88 if (DBG) { 89 Log.d(TAG, "onServerConnectionState() - status=" + status 90 + " serverIf=" + serverIf + " device=" + address); 91 } 92 try { 93 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, 94 connected ? BluetoothProfile.STATE_CONNECTED : 95 BluetoothProfile.STATE_DISCONNECTED); 96 } catch (Exception ex) { 97 Log.w(TAG, "Unhandled exception in callback", ex); 98 } 99 } 100 101 /** 102 * Service has been added 103 * @hide 104 */ 105 @Override 106 public void onServiceAdded(int status, BluetoothGattService service) { 107 if (DBG) { 108 Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId() 109 + " uuid=" + service.getUuid() + " status=" + status); 110 } 111 112 if (mPendingService == null) { 113 return; 114 } 115 116 BluetoothGattService tmp = mPendingService; 117 mPendingService = null; 118 119 // Rewrite newly assigned handles to existing service. 120 tmp.setInstanceId(service.getInstanceId()); 121 List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics(); 122 List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics(); 123 for (int i = 0; i < svc_chars.size(); i++) { 124 BluetoothGattCharacteristic temp_char = temp_chars.get(i); 125 BluetoothGattCharacteristic svc_char = svc_chars.get(i); 126 127 temp_char.setInstanceId(svc_char.getInstanceId()); 128 129 List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors(); 130 List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors(); 131 for (int j = 0; j < svc_descs.size(); j++) { 132 temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId()); 133 } 134 } 135 136 mServices.add(tmp); 137 138 try { 139 mCallback.onServiceAdded((int) status, tmp); 140 } catch (Exception ex) { 141 Log.w(TAG, "Unhandled exception in callback", ex); 142 } 143 } 144 145 /** 146 * Remote client characteristic read request. 147 * @hide 148 */ 149 @Override 150 public void onCharacteristicReadRequest(String address, int transId, 151 int offset, boolean isLong, int handle) { 152 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); 153 154 BluetoothDevice device = mAdapter.getRemoteDevice(address); 155 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); 156 if (characteristic == null) { 157 Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle); 158 return; 159 } 160 161 try { 162 mCallback.onCharacteristicReadRequest(device, transId, offset, 163 characteristic); 164 } catch (Exception ex) { 165 Log.w(TAG, "Unhandled exception in callback", ex); 166 } 167 } 168 169 /** 170 * Remote client descriptor read request. 171 * @hide 172 */ 173 @Override 174 public void onDescriptorReadRequest(String address, int transId, 175 int offset, boolean isLong, int handle) { 176 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); 177 178 BluetoothDevice device = mAdapter.getRemoteDevice(address); 179 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); 180 if (descriptor == null) { 181 Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle); 182 return; 183 } 184 185 try { 186 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); 187 } catch (Exception ex) { 188 Log.w(TAG, "Unhandled exception in callback", ex); 189 } 190 } 191 192 /** 193 * Remote client characteristic write request. 194 * @hide 195 */ 196 @Override 197 public void onCharacteristicWriteRequest(String address, int transId, 198 int offset, int length, boolean isPrep, boolean needRsp, 199 int handle, byte[] value) { 200 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle); 201 202 BluetoothDevice device = mAdapter.getRemoteDevice(address); 203 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); 204 if (characteristic == null) { 205 Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle); 206 return; 207 } 208 209 try { 210 mCallback.onCharacteristicWriteRequest(device, transId, characteristic, 211 isPrep, needRsp, offset, value); 212 } catch (Exception ex) { 213 Log.w(TAG, "Unhandled exception in callback", ex); 214 } 215 216 } 217 218 /** 219 * Remote client descriptor write request. 220 * @hide 221 */ 222 @Override 223 public void onDescriptorWriteRequest(String address, int transId, int offset, 224 int length, boolean isPrep, boolean needRsp, int handle, byte[] value) { 225 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle); 226 227 BluetoothDevice device = mAdapter.getRemoteDevice(address); 228 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); 229 if (descriptor == null) { 230 Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle); 231 return; 232 } 233 234 try { 235 mCallback.onDescriptorWriteRequest(device, transId, descriptor, 236 isPrep, needRsp, offset, value); 237 } catch (Exception ex) { 238 Log.w(TAG, "Unhandled exception in callback", ex); 239 } 240 } 241 242 /** 243 * Execute pending writes. 244 * @hide 245 */ 246 @Override 247 public void onExecuteWrite(String address, int transId, 248 boolean execWrite) { 249 if (DBG) { 250 Log.d(TAG, "onExecuteWrite() - " 251 + "device=" + address + ", transId=" + transId 252 + "execWrite=" + execWrite); 253 } 254 255 BluetoothDevice device = mAdapter.getRemoteDevice(address); 256 if (device == null) return; 257 258 try { 259 mCallback.onExecuteWrite(device, transId, execWrite); 260 } catch (Exception ex) { 261 Log.w(TAG, "Unhandled exception in callback", ex); 262 } 263 } 264 265 /** 266 * A notification/indication has been sent. 267 * @hide 268 */ 269 @Override 270 public void onNotificationSent(String address, int status) { 271 if (VDBG) { 272 Log.d(TAG, "onNotificationSent() - " 273 + "device=" + address + ", status=" + status); 274 } 275 276 BluetoothDevice device = mAdapter.getRemoteDevice(address); 277 if (device == null) return; 278 279 try { 280 mCallback.onNotificationSent(device, status); 281 } catch (Exception ex) { 282 Log.w(TAG, "Unhandled exception: " + ex); 283 } 284 } 285 286 /** 287 * The MTU for a connection has changed 288 * @hide 289 */ 290 @Override 291 public void onMtuChanged(String address, int mtu) { 292 if (DBG) { 293 Log.d(TAG, "onMtuChanged() - " 294 + "device=" + address + ", mtu=" + mtu); 295 } 296 297 BluetoothDevice device = mAdapter.getRemoteDevice(address); 298 if (device == null) return; 299 300 try { 301 mCallback.onMtuChanged(device, mtu); 302 } catch (Exception ex) { 303 Log.w(TAG, "Unhandled exception: " + ex); 304 } 305 } 306 307 /** 308 * The PHY for a connection was updated 309 * @hide 310 */ 311 @Override 312 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { 313 if (DBG) { 314 Log.d(TAG, 315 "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy 316 + ", rxPHy=" + rxPhy); 317 } 318 319 BluetoothDevice device = mAdapter.getRemoteDevice(address); 320 if (device == null) return; 321 322 try { 323 mCallback.onPhyUpdate(device, txPhy, rxPhy, status); 324 } catch (Exception ex) { 325 Log.w(TAG, "Unhandled exception: " + ex); 326 } 327 } 328 329 /** 330 * The PHY for a connection was read 331 * @hide 332 */ 333 @Override 334 public void onPhyRead(String address, int txPhy, int rxPhy, int status) { 335 if (DBG) { 336 Log.d(TAG, 337 "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy 338 + ", rxPHy=" + rxPhy); 339 } 340 341 BluetoothDevice device = mAdapter.getRemoteDevice(address); 342 if (device == null) return; 343 344 try { 345 mCallback.onPhyRead(device, txPhy, rxPhy, status); 346 } catch (Exception ex) { 347 Log.w(TAG, "Unhandled exception: " + ex); 348 } 349 } 350 351 /** 352 * Callback invoked when the given connection is updated 353 * @hide 354 */ 355 @Override 356 public void onConnectionUpdated(String address, int interval, int latency, 357 int timeout, int status) { 358 if (DBG) { 359 Log.d(TAG, "onConnectionUpdated() - Device=" + address 360 + " interval=" + interval + " latency=" + latency 361 + " timeout=" + timeout + " status=" + status); 362 } 363 BluetoothDevice device = mAdapter.getRemoteDevice(address); 364 if (device == null) return; 365 366 try { 367 mCallback.onConnectionUpdated(device, interval, latency, 368 timeout, status); 369 } catch (Exception ex) { 370 Log.w(TAG, "Unhandled exception: " + ex); 371 } 372 } 373 374 }; 375 376 /** 377 * Create a BluetoothGattServer proxy object. 378 */ BluetoothGattServer(IBluetoothGatt iGatt, int transport)379 /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) { 380 mService = iGatt; 381 mAdapter = BluetoothAdapter.getDefaultAdapter(); 382 mCallback = null; 383 mServerIf = 0; 384 mTransport = transport; 385 mServices = new ArrayList<BluetoothGattService>(); 386 } 387 388 /** 389 * Returns a characteristic with given handle. 390 * 391 * @hide 392 */ getCharacteristicByHandle(int handle)393 /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) { 394 for (BluetoothGattService svc : mServices) { 395 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 396 if (charac.getInstanceId() == handle) { 397 return charac; 398 } 399 } 400 } 401 return null; 402 } 403 404 /** 405 * Returns a descriptor with given handle. 406 * 407 * @hide 408 */ getDescriptorByHandle(int handle)409 /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) { 410 for (BluetoothGattService svc : mServices) { 411 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 412 for (BluetoothGattDescriptor desc : charac.getDescriptors()) { 413 if (desc.getInstanceId() == handle) { 414 return desc; 415 } 416 } 417 } 418 } 419 return null; 420 } 421 422 /** 423 * Close this GATT server instance. 424 * 425 * Application should call this method as early as possible after it is done with 426 * this GATT server. 427 */ close()428 public void close() { 429 if (DBG) Log.d(TAG, "close()"); 430 unregisterCallback(); 431 } 432 433 /** 434 * Register an application callback to start using GattServer. 435 * 436 * <p>This is an asynchronous call. The callback is used to notify 437 * success or failure if the function returns true. 438 * 439 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 440 * 441 * @param callback GATT callback handler that will receive asynchronous callbacks. 442 * @return true, the callback will be called to notify success or failure, false on immediate 443 * error 444 */ registerCallback(BluetoothGattServerCallback callback)445 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { 446 if (DBG) Log.d(TAG, "registerCallback()"); 447 if (mService == null) { 448 Log.e(TAG, "GATT service not available"); 449 return false; 450 } 451 UUID uuid = UUID.randomUUID(); 452 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); 453 454 synchronized (mServerIfLock) { 455 if (mCallback != null) { 456 Log.e(TAG, "App can register callback only once"); 457 return false; 458 } 459 460 mCallback = callback; 461 try { 462 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); 463 } catch (RemoteException e) { 464 Log.e(TAG, "", e); 465 mCallback = null; 466 return false; 467 } 468 469 try { 470 mServerIfLock.wait(CALLBACK_REG_TIMEOUT); 471 } catch (InterruptedException e) { 472 Log.e(TAG, "" + e); 473 mCallback = null; 474 } 475 476 if (mServerIf == 0) { 477 mCallback = null; 478 return false; 479 } else { 480 return true; 481 } 482 } 483 } 484 485 /** 486 * Unregister the current application and callbacks. 487 */ unregisterCallback()488 private void unregisterCallback() { 489 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); 490 if (mService == null || mServerIf == 0) return; 491 492 try { 493 mCallback = null; 494 mService.unregisterServer(mServerIf); 495 mServerIf = 0; 496 } catch (RemoteException e) { 497 Log.e(TAG, "", e); 498 } 499 } 500 501 /** 502 * Returns a service by UUID, instance and type. 503 * 504 * @hide 505 */ getService(UUID uuid, int instanceId, int type)506 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { 507 for (BluetoothGattService svc : mServices) { 508 if (svc.getType() == type 509 && svc.getInstanceId() == instanceId 510 && svc.getUuid().equals(uuid)) { 511 return svc; 512 } 513 } 514 return null; 515 } 516 517 /** 518 * Initiate a connection to a Bluetooth GATT capable device. 519 * 520 * <p>The connection may not be established right away, but will be 521 * completed when the remote device is available. A 522 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be 523 * invoked when the connection state changes as a result of this function. 524 * 525 * <p>The autoConnect parameter determines whether to actively connect to 526 * the remote device, or rather passively scan and finalize the connection 527 * when the remote device is in range/available. Generally, the first ever 528 * connection to a device should be direct (autoConnect set to false) and 529 * subsequent connections to known devices should be invoked with the 530 * autoConnect parameter set to true. 531 * 532 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 533 * 534 * @param autoConnect Whether to directly connect to the remote device (false) or to 535 * automatically connect as soon as the remote device becomes available (true). 536 * @return true, if the connection attempt was initiated successfully 537 */ connect(BluetoothDevice device, boolean autoConnect)538 public boolean connect(BluetoothDevice device, boolean autoConnect) { 539 if (DBG) { 540 Log.d(TAG, 541 "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); 542 } 543 if (mService == null || mServerIf == 0) return false; 544 545 try { 546 // autoConnect is inverse of "isDirect" 547 mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport); 548 } catch (RemoteException e) { 549 Log.e(TAG, "", e); 550 return false; 551 } 552 553 return true; 554 } 555 556 /** 557 * Disconnects an established connection, or cancels a connection attempt 558 * currently in progress. 559 * 560 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 561 * 562 * @param device Remote device 563 */ cancelConnection(BluetoothDevice device)564 public void cancelConnection(BluetoothDevice device) { 565 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); 566 if (mService == null || mServerIf == 0) return; 567 568 try { 569 mService.serverDisconnect(mServerIf, device.getAddress()); 570 } catch (RemoteException e) { 571 Log.e(TAG, "", e); 572 } 573 } 574 575 /** 576 * Set the preferred connection PHY for this app. Please note that this is just a 577 * recommendation, whether the PHY change will happen depends on other applications peferences, 578 * local and remote controller capabilities. Controller can override these settings. <p> {@link 579 * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if 580 * no PHY change happens. It is also triggered when remote device updates the PHY. 581 * 582 * @param device The remote device to send this response to 583 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link 584 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 585 * BluetoothDevice#PHY_LE_CODED_MASK}. 586 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link 587 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 588 * BluetoothDevice#PHY_LE_CODED_MASK}. 589 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one 590 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or 591 * {@link BluetoothDevice#PHY_OPTION_S8} 592 */ setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions)593 public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { 594 try { 595 mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, 596 phyOptions); 597 } catch (RemoteException e) { 598 Log.e(TAG, "", e); 599 } 600 } 601 602 /** 603 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned 604 * in {@link BluetoothGattServerCallback#onPhyRead} 605 * 606 * @param device The remote device to send this response to 607 */ readPhy(BluetoothDevice device)608 public void readPhy(BluetoothDevice device) { 609 try { 610 mService.serverReadPhy(mServerIf, device.getAddress()); 611 } catch (RemoteException e) { 612 Log.e(TAG, "", e); 613 } 614 } 615 616 /** 617 * Send a response to a read or write request to a remote device. 618 * 619 * <p>This function must be invoked in when a remote read/write request 620 * is received by one of these callback methods: 621 * 622 * <ul> 623 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} 624 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest} 625 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest} 626 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest} 627 * </ul> 628 * 629 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 630 * 631 * @param device The remote device to send this response to 632 * @param requestId The ID of the request that was received with the callback 633 * @param status The status of the request to be sent to the remote devices 634 * @param offset Value offset for partial read/write response 635 * @param value The value of the attribute that was read/written (optional) 636 */ sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value)637 public boolean sendResponse(BluetoothDevice device, int requestId, 638 int status, int offset, byte[] value) { 639 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); 640 if (mService == null || mServerIf == 0) return false; 641 642 try { 643 mService.sendResponse(mServerIf, device.getAddress(), requestId, 644 status, offset, value); 645 } catch (RemoteException e) { 646 Log.e(TAG, "", e); 647 return false; 648 } 649 return true; 650 } 651 652 /** 653 * Send a notification or indication that a local characteristic has been 654 * updated. 655 * 656 * <p>A notification or indication is sent to the remote device to signal 657 * that the characteristic has been updated. This function should be invoked 658 * for every client that requests notifications/indications by writing 659 * to the "Client Configuration" descriptor for the given characteristic. 660 * 661 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 662 * 663 * @param device The remote device to receive the notification/indication 664 * @param characteristic The local characteristic that has been updated 665 * @param confirm true to request confirmation from the client (indication), false to send a 666 * notification 667 * @return true, if the notification has been triggered successfully 668 * @throws IllegalArgumentException 669 */ notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm)670 public boolean notifyCharacteristicChanged(BluetoothDevice device, 671 BluetoothGattCharacteristic characteristic, boolean confirm) { 672 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); 673 if (mService == null || mServerIf == 0) return false; 674 675 BluetoothGattService service = characteristic.getService(); 676 if (service == null) return false; 677 678 if (characteristic.getValue() == null) { 679 throw new IllegalArgumentException("Chracteristic value is empty. Use " 680 + "BluetoothGattCharacteristic#setvalue to update"); 681 } 682 683 try { 684 mService.sendNotification(mServerIf, device.getAddress(), 685 characteristic.getInstanceId(), confirm, 686 characteristic.getValue()); 687 } catch (RemoteException e) { 688 Log.e(TAG, "", e); 689 return false; 690 } 691 692 return true; 693 } 694 695 /** 696 * Add a service to the list of services to be hosted. 697 * 698 * <p>Once a service has been addded to the list, the service and its 699 * included characteristics will be provided by the local device. 700 * 701 * <p>If the local device has already exposed services when this function 702 * is called, a service update notification will be sent to all clients. 703 * 704 * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate 705 * whether this service has been added successfully. Do not add another service 706 * before this callback. 707 * 708 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 709 * 710 * @param service Service to be added to the list of services provided by this device. 711 * @return true, if the request to add service has been initiated 712 */ addService(BluetoothGattService service)713 public boolean addService(BluetoothGattService service) { 714 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); 715 if (mService == null || mServerIf == 0) return false; 716 717 mPendingService = service; 718 719 try { 720 mService.addService(mServerIf, service); 721 } catch (RemoteException e) { 722 Log.e(TAG, "", e); 723 return false; 724 } 725 726 return true; 727 } 728 729 /** 730 * Removes a service from the list of services to be provided. 731 * 732 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 733 * 734 * @param service Service to be removed. 735 * @return true, if the service has been removed 736 */ removeService(BluetoothGattService service)737 public boolean removeService(BluetoothGattService service) { 738 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); 739 if (mService == null || mServerIf == 0) return false; 740 741 BluetoothGattService intService = getService(service.getUuid(), 742 service.getInstanceId(), service.getType()); 743 if (intService == null) return false; 744 745 try { 746 mService.removeService(mServerIf, service.getInstanceId()); 747 mServices.remove(intService); 748 } catch (RemoteException e) { 749 Log.e(TAG, "", e); 750 return false; 751 } 752 753 return true; 754 } 755 756 /** 757 * Remove all services from the list of provided services. 758 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 759 */ clearServices()760 public void clearServices() { 761 if (DBG) Log.d(TAG, "clearServices()"); 762 if (mService == null || mServerIf == 0) return; 763 764 try { 765 mService.clearServices(mServerIf); 766 mServices.clear(); 767 } catch (RemoteException e) { 768 Log.e(TAG, "", e); 769 } 770 } 771 772 /** 773 * Returns a list of GATT services offered by this device. 774 * 775 * <p>An application must call {@link #addService} to add a serice to the 776 * list of services offered by this device. 777 * 778 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 779 * 780 * @return List of services. Returns an empty list if no services have been added yet. 781 */ getServices()782 public List<BluetoothGattService> getServices() { 783 return mServices; 784 } 785 786 /** 787 * Returns a {@link BluetoothGattService} from the list of services offered 788 * by this device. 789 * 790 * <p>If multiple instances of the same service (as identified by UUID) 791 * exist, the first instance of the service is returned. 792 * 793 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 794 * 795 * @param uuid UUID of the requested service 796 * @return BluetoothGattService if supported, or null if the requested service is not offered by 797 * this device. 798 */ getService(UUID uuid)799 public BluetoothGattService getService(UUID uuid) { 800 for (BluetoothGattService service : mServices) { 801 if (service.getUuid().equals(uuid)) { 802 return service; 803 } 804 } 805 806 return null; 807 } 808 809 810 /** 811 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 812 * with {@link BluetoothProfile#GATT} as argument 813 * 814 * @throws UnsupportedOperationException 815 */ 816 @Override getConnectionState(BluetoothDevice device)817 public int getConnectionState(BluetoothDevice device) { 818 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 819 } 820 821 /** 822 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 823 * with {@link BluetoothProfile#GATT} as argument 824 * 825 * @throws UnsupportedOperationException 826 */ 827 @Override getConnectedDevices()828 public List<BluetoothDevice> getConnectedDevices() { 829 throw new UnsupportedOperationException( 830 "Use BluetoothManager#getConnectedDevices instead."); 831 } 832 833 /** 834 * Not supported - please use 835 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 836 * with {@link BluetoothProfile#GATT} as first argument 837 * 838 * @throws UnsupportedOperationException 839 */ 840 @Override getDevicesMatchingConnectionStates(int[] states)841 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 842 throw new UnsupportedOperationException( 843 "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 844 } 845 } 846