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