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 * The MTU for a connection has changed 290 * @hide 291 */ 292 public void onMtuChanged(String address, int mtu) { 293 if (DBG) Log.d(TAG, "onMtuChanged() - " 294 + "device=" + address + ", mtu=" + mtu); 295 296 BluetoothDevice device = mAdapter.getRemoteDevice(address); 297 if (device == null) return; 298 299 try { 300 mCallback.onMtuChanged(device, mtu); 301 } catch (Exception ex) { 302 Log.w(TAG, "Unhandled exception: " + ex); 303 } 304 } 305 }; 306 307 /** 308 * Create a BluetoothGattServer proxy object. 309 */ 310 /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) { 311 mContext = context; 312 mService = iGatt; 313 mAdapter = BluetoothAdapter.getDefaultAdapter(); 314 mCallback = null; 315 mServerIf = 0; 316 mTransport = transport; 317 mServices = new ArrayList<BluetoothGattService>(); 318 } 319 320 /** 321 * Close this GATT server instance. 322 * 323 * Application should call this method as early as possible after it is done with 324 * this GATT server. 325 */ 326 public void close() { 327 if (DBG) Log.d(TAG, "close()"); 328 unregisterCallback(); 329 } 330 331 /** 332 * Register an application callback to start using GattServer. 333 * 334 * <p>This is an asynchronous call. The callback is used to notify 335 * success or failure if the function returns true. 336 * 337 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 338 * 339 * @param callback GATT callback handler that will receive asynchronous 340 * callbacks. 341 * @return true, the callback will be called to notify success or failure, 342 * false on immediate error 343 */ 344 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { 345 if (DBG) Log.d(TAG, "registerCallback()"); 346 if (mService == null) { 347 Log.e(TAG, "GATT service not available"); 348 return false; 349 } 350 UUID uuid = UUID.randomUUID(); 351 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); 352 353 synchronized(mServerIfLock) { 354 if (mCallback != null) { 355 Log.e(TAG, "App can register callback only once"); 356 return false; 357 } 358 359 mCallback = callback; 360 try { 361 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); 362 } catch (RemoteException e) { 363 Log.e(TAG,"",e); 364 mCallback = null; 365 return false; 366 } 367 368 try { 369 mServerIfLock.wait(CALLBACK_REG_TIMEOUT); 370 } catch (InterruptedException e) { 371 Log.e(TAG, "" + e); 372 mCallback = null; 373 } 374 375 if (mServerIf == 0) { 376 mCallback = null; 377 return false; 378 } else { 379 return true; 380 } 381 } 382 } 383 384 /** 385 * Unregister the current application and callbacks. 386 */ 387 private void unregisterCallback() { 388 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); 389 if (mService == null || mServerIf == 0) return; 390 391 try { 392 mCallback = null; 393 mService.unregisterServer(mServerIf); 394 mServerIf = 0; 395 } catch (RemoteException e) { 396 Log.e(TAG,"",e); 397 } 398 } 399 400 /** 401 * Returns a service by UUID, instance and type. 402 * @hide 403 */ 404 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { 405 for(BluetoothGattService svc : mServices) { 406 if (svc.getType() == type && 407 svc.getInstanceId() == instanceId && 408 svc.getUuid().equals(uuid)) { 409 return svc; 410 } 411 } 412 return null; 413 } 414 415 /** 416 * Initiate a connection to a Bluetooth GATT capable device. 417 * 418 * <p>The connection may not be established right away, but will be 419 * completed when the remote device is available. A 420 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be 421 * invoked when the connection state changes as a result of this function. 422 * 423 * <p>The autoConnect paramter determines whether to actively connect to 424 * the remote device, or rather passively scan and finalize the connection 425 * when the remote device is in range/available. Generally, the first ever 426 * connection to a device should be direct (autoConnect set to false) and 427 * subsequent connections to known devices should be invoked with the 428 * autoConnect parameter set to true. 429 * 430 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 431 * 432 * @param autoConnect Whether to directly connect to the remote device (false) 433 * or to automatically connect as soon as the remote 434 * device becomes available (true). 435 * @return true, if the connection attempt was initiated successfully 436 */ 437 public boolean connect(BluetoothDevice device, boolean autoConnect) { 438 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); 439 if (mService == null || mServerIf == 0) return false; 440 441 try { 442 mService.serverConnect(mServerIf, device.getAddress(), 443 autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect" 444 } catch (RemoteException e) { 445 Log.e(TAG,"",e); 446 return false; 447 } 448 449 return true; 450 } 451 452 /** 453 * Disconnects an established connection, or cancels a connection attempt 454 * currently in progress. 455 * 456 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 457 * 458 * @param device Remote device 459 */ 460 public void cancelConnection(BluetoothDevice device) { 461 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); 462 if (mService == null || mServerIf == 0) return; 463 464 try { 465 mService.serverDisconnect(mServerIf, device.getAddress()); 466 } catch (RemoteException e) { 467 Log.e(TAG,"",e); 468 } 469 } 470 471 /** 472 * Send a response to a read or write request to a remote device. 473 * 474 * <p>This function must be invoked in when a remote read/write request 475 * is received by one of these callback methods: 476 * 477 * <ul> 478 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} 479 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest} 480 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest} 481 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest} 482 * </ul> 483 * 484 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 485 * 486 * @param device The remote device to send this response to 487 * @param requestId The ID of the request that was received with the callback 488 * @param status The status of the request to be sent to the remote devices 489 * @param offset Value offset for partial read/write response 490 * @param value The value of the attribute that was read/written (optional) 491 */ 492 public boolean sendResponse(BluetoothDevice device, int requestId, 493 int status, int offset, byte[] value) { 494 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); 495 if (mService == null || mServerIf == 0) return false; 496 497 try { 498 mService.sendResponse(mServerIf, device.getAddress(), requestId, 499 status, offset, value); 500 } catch (RemoteException e) { 501 Log.e(TAG,"",e); 502 return false; 503 } 504 return true; 505 } 506 507 /** 508 * Send a notification or indication that a local characteristic has been 509 * updated. 510 * 511 * <p>A notification or indication is sent to the remote device to signal 512 * that the characteristic has been updated. This function should be invoked 513 * for every client that requests notifications/indications by writing 514 * to the "Client Configuration" descriptor for the given characteristic. 515 * 516 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 517 * 518 * @param device The remote device to receive the notification/indication 519 * @param characteristic The local characteristic that has been updated 520 * @param confirm true to request confirmation from the client (indication), 521 * false to send a notification 522 * @throws IllegalArgumentException 523 * @return true, if the notification has been triggered successfully 524 */ 525 public boolean notifyCharacteristicChanged(BluetoothDevice device, 526 BluetoothGattCharacteristic characteristic, boolean confirm) { 527 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); 528 if (mService == null || mServerIf == 0) return false; 529 530 BluetoothGattService service = characteristic.getService(); 531 if (service == null) return false; 532 533 if (characteristic.getValue() == null) { 534 throw new IllegalArgumentException("Chracteristic value is empty. Use " 535 + "BluetoothGattCharacteristic#setvalue to update"); 536 } 537 538 try { 539 mService.sendNotification(mServerIf, device.getAddress(), 540 service.getType(), service.getInstanceId(), 541 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(), 542 new ParcelUuid(characteristic.getUuid()), confirm, 543 characteristic.getValue()); 544 } catch (RemoteException e) { 545 Log.e(TAG,"",e); 546 return false; 547 } 548 549 return true; 550 } 551 552 /** 553 * Add a service to the list of services to be hosted. 554 * 555 * <p>Once a service has been addded to the the list, the service and its 556 * included characteristics will be provided by the local device. 557 * 558 * <p>If the local device has already exposed services when this function 559 * is called, a service update notification will be sent to all clients. 560 * 561 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 562 * 563 * @param service Service to be added to the list of services provided 564 * by this device. 565 * @return true, if the service has been added successfully 566 */ 567 public boolean addService(BluetoothGattService service) { 568 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); 569 if (mService == null || mServerIf == 0) return false; 570 571 mServices.add(service); 572 573 try { 574 mService.beginServiceDeclaration(mServerIf, service.getType(), 575 service.getInstanceId(), service.getHandles(), 576 new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); 577 578 List<BluetoothGattService> includedServices = service.getIncludedServices(); 579 for (BluetoothGattService includedService : includedServices) { 580 mService.addIncludedService(mServerIf, 581 includedService.getType(), 582 includedService.getInstanceId(), 583 new ParcelUuid(includedService.getUuid())); 584 } 585 586 List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics(); 587 for (BluetoothGattCharacteristic characteristic : characteristics) { 588 int permission = ((characteristic.getKeySize() - 7) << 12) 589 + characteristic.getPermissions(); 590 mService.addCharacteristic(mServerIf, 591 new ParcelUuid(characteristic.getUuid()), 592 characteristic.getProperties(), permission); 593 594 List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors(); 595 for (BluetoothGattDescriptor descriptor: descriptors) { 596 permission = ((characteristic.getKeySize() - 7) << 12) 597 + descriptor.getPermissions(); 598 mService.addDescriptor(mServerIf, 599 new ParcelUuid(descriptor.getUuid()), permission); 600 } 601 } 602 603 mService.endServiceDeclaration(mServerIf); 604 } catch (RemoteException e) { 605 Log.e(TAG,"",e); 606 return false; 607 } 608 609 return true; 610 } 611 612 /** 613 * Removes a service from the list of services to be provided. 614 * 615 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 616 * 617 * @param service Service to be removed. 618 * @return true, if the service has been removed 619 */ 620 public boolean removeService(BluetoothGattService service) { 621 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); 622 if (mService == null || mServerIf == 0) return false; 623 624 BluetoothGattService intService = getService(service.getUuid(), 625 service.getInstanceId(), service.getType()); 626 if (intService == null) return false; 627 628 try { 629 mService.removeService(mServerIf, service.getType(), 630 service.getInstanceId(), new ParcelUuid(service.getUuid())); 631 mServices.remove(intService); 632 } catch (RemoteException e) { 633 Log.e(TAG,"",e); 634 return false; 635 } 636 637 return true; 638 } 639 640 /** 641 * Remove all services from the list of provided services. 642 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 643 */ 644 public void clearServices() { 645 if (DBG) Log.d(TAG, "clearServices()"); 646 if (mService == null || mServerIf == 0) return; 647 648 try { 649 mService.clearServices(mServerIf); 650 mServices.clear(); 651 } catch (RemoteException e) { 652 Log.e(TAG,"",e); 653 } 654 } 655 656 /** 657 * Returns a list of GATT services offered by this device. 658 * 659 * <p>An application must call {@link #addService} to add a serice to the 660 * list of services offered by this device. 661 * 662 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 663 * 664 * @return List of services. Returns an empty list 665 * if no services have been added yet. 666 */ 667 public List<BluetoothGattService> getServices() { 668 return mServices; 669 } 670 671 /** 672 * Returns a {@link BluetoothGattService} from the list of services offered 673 * by this device. 674 * 675 * <p>If multiple instances of the same service (as identified by UUID) 676 * exist, the first instance of the service is returned. 677 * 678 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 679 * 680 * @param uuid UUID of the requested service 681 * @return BluetoothGattService if supported, or null if the requested 682 * service is not offered by this device. 683 */ 684 public BluetoothGattService getService(UUID uuid) { 685 for (BluetoothGattService service : mServices) { 686 if (service.getUuid().equals(uuid)) { 687 return service; 688 } 689 } 690 691 return null; 692 } 693 694 695 /** 696 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 697 * with {@link BluetoothProfile#GATT} as argument 698 * 699 * @throws UnsupportedOperationException 700 */ 701 @Override 702 public int getConnectionState(BluetoothDevice device) { 703 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 704 } 705 706 /** 707 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} 708 * with {@link BluetoothProfile#GATT} as argument 709 * 710 * @throws UnsupportedOperationException 711 */ 712 @Override 713 public List<BluetoothDevice> getConnectedDevices() { 714 throw new UnsupportedOperationException 715 ("Use BluetoothManager#getConnectedDevices instead."); 716 } 717 718 /** 719 * Not supported - please use 720 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} 721 * with {@link BluetoothProfile#GATT} as first argument 722 * 723 * @throws UnsupportedOperationException 724 */ 725 @Override 726 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 727 throw new UnsupportedOperationException 728 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 729 } 730 } 731