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