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