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 final Context mContext; 48 private BluetoothAdapter mAdapter; 49 private IBluetoothGatt mService; 50 private BluetoothGattServerCallback mCallback; 51 52 private Object mServerIfLock = new Object(); 53 private int mServerIf; 54 private int mTransport; 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 public void onServerRegistered(int status, int serverIf) { 69 if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status 70 + " serverIf=" + serverIf); 71 synchronized(mServerIfLock) { 72 if (mCallback != null) { 73 mServerIf = serverIf; 74 mServerIfLock.notify(); 75 } else { 76 // registration timeout 77 Log.e(TAG, "onServerRegistered: mCallback is null"); 78 } 79 } 80 } 81 82 /** 83 * Callback reporting an LE scan result. 84 * @hide 85 */ 86 public void onScanResult(String address, int rssi, byte[] advData) { 87 if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); 88 // no op 89 } 90 91 /** 92 * Server connection state changed 93 * @hide 94 */ 95 public void onServerConnectionState(int status, int serverIf, 96 boolean connected, String address) { 97 if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status 98 + " serverIf=" + serverIf + " device=" + address); 99 try { 100 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, 101 connected ? BluetoothProfile.STATE_CONNECTED : 102 BluetoothProfile.STATE_DISCONNECTED); 103 } catch (Exception ex) { 104 Log.w(TAG, "Unhandled exception in callback", ex); 105 } 106 } 107 108 /** 109 * Service has been added 110 * @hide 111 */ 112 public void onServiceAdded(int status, int srvcType, 113 int srvcInstId, ParcelUuid srvcId) { 114 UUID srvcUuid = srvcId.getUuid(); 115 if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid 116 + "status=" + status); 117 118 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); 119 if (service == null) return; 120 121 try { 122 mCallback.onServiceAdded((int)status, service); 123 } catch (Exception ex) { 124 Log.w(TAG, "Unhandled exception in callback", ex); 125 } 126 } 127 128 /** 129 * Remote client characteristic read request. 130 * @hide 131 */ 132 public void onCharacteristicReadRequest(String address, int transId, 133 int offset, boolean isLong, int srvcType, int srvcInstId, 134 ParcelUuid srvcId, int charInstId, ParcelUuid charId) { 135 UUID srvcUuid = srvcId.getUuid(); 136 UUID charUuid = charId.getUuid(); 137 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - " 138 + "service=" + srvcUuid + ", characteristic=" + charUuid); 139 140 BluetoothDevice device = mAdapter.getRemoteDevice(address); 141 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); 142 if (service == null) return; 143 144 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); 145 if (characteristic == null) return; 146 147 try { 148 mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic); 149 } catch (Exception ex) { 150 Log.w(TAG, "Unhandled exception in callback", ex); 151 } 152 } 153 154 /** 155 * Remote client descriptor read request. 156 * @hide 157 */ 158 public void onDescriptorReadRequest(String address, int transId, 159 int offset, boolean isLong, int srvcType, int srvcInstId, 160 ParcelUuid srvcId, int charInstId, ParcelUuid charId, 161 ParcelUuid descrId) { 162 UUID srvcUuid = srvcId.getUuid(); 163 UUID charUuid = charId.getUuid(); 164 UUID descrUuid = descrId.getUuid(); 165 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - " 166 + "service=" + srvcUuid + ", characteristic=" + charUuid 167 + "descriptor=" + descrUuid); 168 169 BluetoothDevice device = mAdapter.getRemoteDevice(address); 170 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); 171 if (service == null) return; 172 173 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); 174 if (characteristic == null) return; 175 176 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid); 177 if (descriptor == null) return; 178 179 try { 180 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); 181 } catch (Exception ex) { 182 Log.w(TAG, "Unhandled exception in callback", ex); 183 } 184 } 185 186 /** 187 * Remote client characteristic write request. 188 * @hide 189 */ 190 public void onCharacteristicWriteRequest(String address, int transId, 191 int offset, int length, boolean isPrep, boolean needRsp, 192 int srvcType, int srvcInstId, ParcelUuid srvcId, 193 int charInstId, ParcelUuid charId, byte[] value) { 194 UUID srvcUuid = srvcId.getUuid(); 195 UUID charUuid = charId.getUuid(); 196 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - " 197 + "service=" + srvcUuid + ", characteristic=" + charUuid); 198 199 BluetoothDevice device = mAdapter.getRemoteDevice(address); 200 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); 201 if (service == null) return; 202 203 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); 204 if (characteristic == null) return; 205 206 try { 207 mCallback.onCharacteristicWriteRequest(device, transId, characteristic, 208 isPrep, needRsp, offset, value); 209 } catch (Exception ex) { 210 Log.w(TAG, "Unhandled exception in callback", ex); 211 } 212 213 } 214 215 /** 216 * Remote client descriptor write request. 217 * @hide 218 */ 219 public void onDescriptorWriteRequest(String address, int transId, 220 int offset, int length, boolean isPrep, boolean needRsp, 221 int srvcType, int srvcInstId, ParcelUuid srvcId, 222 int charInstId, ParcelUuid charId, ParcelUuid descrId, 223 byte[] value) { 224 UUID srvcUuid = srvcId.getUuid(); 225 UUID charUuid = charId.getUuid(); 226 UUID descrUuid = descrId.getUuid(); 227 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - " 228 + "service=" + srvcUuid + ", characteristic=" + charUuid 229 + "descriptor=" + descrUuid); 230 231 BluetoothDevice device = mAdapter.getRemoteDevice(address); 232 233 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); 234 if (service == null) return; 235 236 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); 237 if (characteristic == null) return; 238 239 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid); 240 if (descriptor == null) return; 241 242 try { 243 mCallback.onDescriptorWriteRequest(device, transId, descriptor, 244 isPrep, needRsp, offset, value); 245 } catch (Exception ex) { 246 Log.w(TAG, "Unhandled exception in callback", ex); 247 } 248 } 249 250 /** 251 * Execute pending writes. 252 * @hide 253 */ 254 public void onExecuteWrite(String address, int transId, 255 boolean execWrite) { 256 if (DBG) Log.d(TAG, "onExecuteWrite() - " 257 + "device=" + address + ", transId=" + transId 258 + "execWrite=" + execWrite); 259 260 BluetoothDevice device = mAdapter.getRemoteDevice(address); 261 if (device == null) return; 262 263 try { 264 mCallback.onExecuteWrite(device, transId, execWrite); 265 } catch (Exception ex) { 266 Log.w(TAG, "Unhandled exception in callback", ex); 267 } 268 } 269 270 /** 271 * A notification/indication has been sent. 272 * @hide 273 */ 274 public void onNotificationSent(String address, int status) { 275 if (VDBG) Log.d(TAG, "onNotificationSent() - " 276 + "device=" + address + ", status=" + status); 277 278 BluetoothDevice device = mAdapter.getRemoteDevice(address); 279 if (device == null) return; 280 281 try { 282 mCallback.onNotificationSent(device, status); 283 } catch (Exception ex) { 284 Log.w(TAG, "Unhandled exception: " + ex); 285 } 286 } 287 }; 288 289 /** 290 * Create a BluetoothGattServer proxy object. 291 */ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport)292 /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) { 293 mContext = context; 294 mService = iGatt; 295 mAdapter = BluetoothAdapter.getDefaultAdapter(); 296 mCallback = null; 297 mServerIf = 0; 298 mTransport = transport; 299 mServices = new ArrayList<BluetoothGattService>(); 300 } 301 302 /** 303 * Close this GATT server instance. 304 * 305 * Application should call this method as early as possible after it is done with 306 * this GATT server. 307 */ close()308 public void close() { 309 if (DBG) Log.d(TAG, "close()"); 310 unregisterCallback(); 311 } 312 313 /** 314 * Register an application callback to start using GattServer. 315 * 316 * <p>This is an asynchronous call. The callback is used to notify 317 * success or failure if the function returns true. 318 * 319 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 320 * 321 * @param callback GATT callback handler that will receive asynchronous 322 * callbacks. 323 * @return true, the callback will be called to notify success or failure, 324 * false on immediate error 325 */ registerCallback(BluetoothGattServerCallback callback)326 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { 327 if (DBG) Log.d(TAG, "registerCallback()"); 328 if (mService == null) { 329 Log.e(TAG, "GATT service not available"); 330 return false; 331 } 332 UUID uuid = UUID.randomUUID(); 333 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); 334 335 synchronized(mServerIfLock) { 336 if (mCallback != null) { 337 Log.e(TAG, "App can register callback only once"); 338 return false; 339 } 340 341 mCallback = callback; 342 try { 343 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); 344 } catch (RemoteException e) { 345 Log.e(TAG,"",e); 346 mCallback = null; 347 return false; 348 } 349 350 try { 351 mServerIfLock.wait(CALLBACK_REG_TIMEOUT); 352 } catch (InterruptedException e) { 353 Log.e(TAG, "" + e); 354 mCallback = null; 355 } 356 357 if (mServerIf == 0) { 358 mCallback = null; 359 return false; 360 } else { 361 return true; 362 } 363 } 364 } 365 366 /** 367 * Unregister the current application and callbacks. 368 */ unregisterCallback()369 private void unregisterCallback() { 370 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); 371 if (mService == null || mServerIf == 0) return; 372 373 try { 374 mCallback = null; 375 mService.unregisterServer(mServerIf); 376 mServerIf = 0; 377 } catch (RemoteException e) { 378 Log.e(TAG,"",e); 379 } 380 } 381 382 /** 383 * Returns a service by UUID, instance and type. 384 * @hide 385 */ getService(UUID uuid, int instanceId, int type)386 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { 387 for(BluetoothGattService svc : mServices) { 388 if (svc.getType() == type && 389 svc.getInstanceId() == instanceId && 390 svc.getUuid().equals(uuid)) { 391 return svc; 392 } 393 } 394 return null; 395 } 396 397 /** 398 * Initiate a connection to a Bluetooth GATT capable device. 399 * 400 * <p>The connection may not be established right away, but will be 401 * completed when the remote device is available. A 402 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be 403 * invoked when the connection state changes as a result of this function. 404 * 405 * <p>The autoConnect paramter determines whether to actively connect to 406 * the remote device, or rather passively scan and finalize the connection 407 * when the remote device is in range/available. Generally, the first ever 408 * connection to a device should be direct (autoConnect set to false) and 409 * subsequent connections to known devices should be invoked with the 410 * autoConnect parameter set to true. 411 * 412 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 413 * 414 * @param autoConnect Whether to directly connect to the remote device (false) 415 * or to automatically connect as soon as the remote 416 * device becomes available (true). 417 * @return true, if the connection attempt was initiated successfully 418 */ connect(BluetoothDevice device, boolean autoConnect)419 public boolean connect(BluetoothDevice device, boolean autoConnect) { 420 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); 421 if (mService == null || mServerIf == 0) return false; 422 423 try { 424 mService.serverConnect(mServerIf, device.getAddress(), 425 autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect" 426 } catch (RemoteException e) { 427 Log.e(TAG,"",e); 428 return false; 429 } 430 431 return true; 432 } 433 434 /** 435 * Disconnects an established connection, or cancels a connection attempt 436 * currently in progress. 437 * 438 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 439 * 440 * @param device Remote device 441 */ cancelConnection(BluetoothDevice device)442 public void cancelConnection(BluetoothDevice device) { 443 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); 444 if (mService == null || mServerIf == 0) return; 445 446 try { 447 mService.serverDisconnect(mServerIf, device.getAddress()); 448 } catch (RemoteException e) { 449 Log.e(TAG,"",e); 450 } 451 } 452 453 /** 454 * Send a response to a read or write request to a remote device. 455 * 456 * <p>This function must be invoked in when a remote read/write request 457 * is received by one of these callback methods: 458 * 459 * <ul> 460 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} 461 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest} 462 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest} 463 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest} 464 * </ul> 465 * 466 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 467 * 468 * @param device The remote device to send this response to 469 * @param requestId The ID of the request that was received with the callback 470 * @param status The status of the request to be sent to the remote devices 471 * @param offset Value offset for partial read/write response 472 * @param value The value of the attribute that was read/written (optional) 473 */ sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value)474 public boolean sendResponse(BluetoothDevice device, int requestId, 475 int status, int offset, byte[] value) { 476 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); 477 if (mService == null || mServerIf == 0) return false; 478 479 try { 480 mService.sendResponse(mServerIf, device.getAddress(), requestId, 481 status, offset, value); 482 } catch (RemoteException e) { 483 Log.e(TAG,"",e); 484 return false; 485 } 486 return true; 487 } 488 489 /** 490 * Send a notification or indication that a local characteristic has been 491 * updated. 492 * 493 * <p>A notification or indication is sent to the remote device to signal 494 * that the characteristic has been updated. This function should be invoked 495 * for every client that requests notifications/indications by writing 496 * to the "Client Configuration" descriptor for the given characteristic. 497 * 498 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 499 * 500 * @param device The remote device to receive the notification/indication 501 * @param characteristic The local characteristic that has been updated 502 * @param confirm true to request confirmation from the client (indication), 503 * false to send a notification 504 * @throws IllegalArgumentException 505 * @return true, if the notification has been triggered successfully 506 */ notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm)507 public boolean notifyCharacteristicChanged(BluetoothDevice device, 508 BluetoothGattCharacteristic characteristic, boolean confirm) { 509 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); 510 if (mService == null || mServerIf == 0) return false; 511 512 BluetoothGattService service = characteristic.getService(); 513 if (service == null) return false; 514 515 if (characteristic.getValue() == null) { 516 throw new IllegalArgumentException("Chracteristic value is empty. Use " 517 + "BluetoothGattCharacteristic#setvalue to update"); 518 } 519 520 try { 521 mService.sendNotification(mServerIf, device.getAddress(), 522 service.getType(), service.getInstanceId(), 523 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 524 new ParcelUuid(characteristic.getUuid()), confirm, 525 characteristic.getValue()); 526 } catch (RemoteException e) { 527 Log.e(TAG,"",e); 528 return false; 529 } 530 531 return true; 532 } 533 534 /** 535 * Add a service to the list of services to be hosted. 536 * 537 * <p>Once a service has been addded to the the list, the service and its 538 * included characteristics will be provided by the local device. 539 * 540 * <p>If the local device has already exposed services when this function 541 * is called, a service update notification will be sent to all clients. 542 * 543 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 544 * 545 * @param service Service to be added to the list of services provided 546 * by this device. 547 * @return true, if the service has been added successfully 548 */ addService(BluetoothGattService service)549 public boolean addService(BluetoothGattService service) { 550 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); 551 if (mService == null || mServerIf == 0) return false; 552 553 mServices.add(service); 554 555 try { 556 mService.beginServiceDeclaration(mServerIf, service.getType(), 557 service.getInstanceId(), service.getHandles(), 558 new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); 559 560 List<BluetoothGattService> includedServices = service.getIncludedServices(); 561 for (BluetoothGattService includedService : includedServices) { 562 mService.addIncludedService(mServerIf, 563 includedService.getType(), 564 includedService.getInstanceId(), 565 new ParcelUuid(includedService.getUuid())); 566 } 567 568 List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics(); 569 for (BluetoothGattCharacteristic characteristic : characteristics) { 570 int permission = ((characteristic.getKeySize() - 7) << 12) 571 + characteristic.getPermissions(); 572 mService.addCharacteristic(mServerIf, 573 new ParcelUuid(characteristic.getUuid()), 574 characteristic.getProperties(), permission); 575 576 List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors(); 577 for (BluetoothGattDescriptor descriptor: descriptors) { 578 permission = ((characteristic.getKeySize() - 7) << 12) 579 + descriptor.getPermissions(); 580 mService.addDescriptor(mServerIf, 581 new ParcelUuid(descriptor.getUuid()), permission); 582 } 583 } 584 585 mService.endServiceDeclaration(mServerIf); 586 } catch (RemoteException e) { 587 Log.e(TAG,"",e); 588 return false; 589 } 590 591 return true; 592 } 593 594 /** 595 * Removes a service from the list of services to be provided. 596 * 597 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 598 * 599 * @param service Service to be removed. 600 * @return true, if the service has been removed 601 */ removeService(BluetoothGattService service)602 public boolean removeService(BluetoothGattService service) { 603 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); 604 if (mService == null || mServerIf == 0) return false; 605 606 BluetoothGattService intService = getService(service.getUuid(), 607 service.getInstanceId(), service.getType()); 608 if (intService == null) return false; 609 610 try { 611 mService.removeService(mServerIf, service.getType(), 612 service.getInstanceId(), new ParcelUuid(service.getUuid())); 613 mServices.remove(intService); 614 } catch (RemoteException e) { 615 Log.e(TAG,"",e); 616 return false; 617 } 618 619 return true; 620 } 621 622 /** 623 * Remove all services from the list of provided services. 624 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 625 */ clearServices()626 public void clearServices() { 627 if (DBG) Log.d(TAG, "clearServices()"); 628 if (mService == null || mServerIf == 0) return; 629 630 try { 631 mService.clearServices(mServerIf); 632 mServices.clear(); 633 } catch (RemoteException e) { 634 Log.e(TAG,"",e); 635 } 636 } 637 638 /** 639 * Returns a list of GATT services offered by this device. 640 * 641 * <p>An application must call {@link #addService} to add a serice to the 642 * list of services offered by this device. 643 * 644 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 645 * 646 * @return List of services. Returns an empty list 647 * if no services have been added yet. 648 */ getServices()649 public List<BluetoothGattService> getServices() { 650 return mServices; 651 } 652 653 /** 654 * Returns a {@link BluetoothGattService} from the list of services offered 655 * by this device. 656 * 657 * <p>If multiple instances of the same service (as identified by UUID) 658 * exist, the first instance of the service is returned. 659 * 660 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 661 * 662 * @param uuid UUID of the requested service 663 * @return BluetoothGattService if supported, or null if the requested 664 * service is not offered by this device. 665 */ getService(UUID uuid)666 public BluetoothGattService getService(UUID uuid) { 667 for (BluetoothGattService service : mServices) { 668 if (service.getUuid().equals(uuid)) { 669 return service; 670 } 671 } 672 673 return null; 674 } 675 676 677 /** 678 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 679 * with {@link BluetoothProfile#GATT} as argument 680 * 681 * @throws UnsupportedOperationException 682 */ 683 @Override getConnectionState(BluetoothDevice device)684 public int getConnectionState(BluetoothDevice device) { 685 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 686 } 687 688 /** 689 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 690 * with {@link BluetoothProfile#GATT} as argument 691 * 692 * @throws UnsupportedOperationException 693 */ 694 @Override getConnectedDevices()695 public List<BluetoothDevice> getConnectedDevices() { 696 throw new UnsupportedOperationException 697 ("Use BluetoothManager#getConnectedDevices instead."); 698 } 699 700 /** 701 * Not supported - please use 702 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 703 * with {@link BluetoothProfile#GATT} as first argument 704 * 705 * @throws UnsupportedOperationException 706 */ 707 @Override getDevicesMatchingConnectionStates(int[] states)708 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 709 throw new UnsupportedOperationException 710 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 711 } 712 } 713