• 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.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