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