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