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