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.content.Context;
     20 import android.os.ParcelUuid;
     21 import android.os.RemoteException;
     22 import android.util.Log;
     23 
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 import java.util.UUID;
     27 
     28 /**
     29  * Public API for the Bluetooth GATT Profile.
     30  *
     31  * <p>This class provides Bluetooth GATT functionality to enable communication
     32  * with Bluetooth Smart or Smart Ready devices.
     33  *
     34  * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
     35  * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
     36  * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
     37  * scan process.
     38  */
     39 public final class BluetoothGatt implements BluetoothProfile {
     40     private static final String TAG = "BluetoothGatt";
     41     private static final boolean DBG = true;
     42     private static final boolean VDBG = false;
     43 
     44     private final Context mContext;
     45     private IBluetoothGatt mService;
     46     private BluetoothGattCallback mCallback;
     47     private int mClientIf;
     48     private boolean mAuthRetry = false;
     49     private BluetoothDevice mDevice;
     50     private boolean mAutoConnect;
     51     private int mConnState;
     52     private final Object mStateLock = new Object();
     53     private Boolean mDeviceBusy = false;
     54     private int mTransport;
     55 
     56     private static final int CONN_STATE_IDLE = 0;
     57     private static final int CONN_STATE_CONNECTING = 1;
     58     private static final int CONN_STATE_CONNECTED = 2;
     59     private static final int CONN_STATE_DISCONNECTING = 3;
     60     private static final int CONN_STATE_CLOSED = 4;
     61 
     62     private List<BluetoothGattService> mServices;
     63 
     64     /** A GATT operation completed successfully */
     65     public static final int GATT_SUCCESS = 0;
     66 
     67     /** GATT read operation is not permitted */
     68     public static final int GATT_READ_NOT_PERMITTED = 0x2;
     69 
     70     /** GATT write operation is not permitted */
     71     public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
     72 
     73     /** Insufficient authentication for a given operation */
     74     public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
     75 
     76     /** The given request is not supported */
     77     public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
     78 
     79     /** Insufficient encryption for a given operation */
     80     public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
     81 
     82     /** A read or write operation was requested with an invalid offset */
     83     public static final int GATT_INVALID_OFFSET = 0x7;
     84 
     85     /** A write operation exceeds the maximum length of the attribute */
     86     public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
     87 
     88     /** A remote device connection is congested. */
     89     public static final int GATT_CONNECTION_CONGESTED = 0x8f;
     90 
     91     /** A GATT operation failed, errors other than the above */
     92     public static final int GATT_FAILURE = 0x101;
     93 
     94     /**
     95      * Connection paramter update - Use the connection paramters recommended by the
     96      * Bluetooth SIG. This is the default value if no connection parameter update
     97      * is requested.
     98      */
     99     public static final int CONNECTION_PRIORITY_BALANCED = 0;
    100 
    101     /**
    102      * Connection paramter update - Request a high priority, low latency connection.
    103      * An application should only request high priority connection paramters to transfer
    104      * large amounts of data over LE quickly. Once the transfer is complete, the application
    105      * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
    106      * to reduce energy use.
    107      */
    108     public static final int CONNECTION_PRIORITY_HIGH = 1;
    109 
    110     /** Connection paramter update - Request low power, reduced data rate connection parameters. */
    111     public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
    112 
    113     /**
    114      * No authentication required.
    115      * @hide
    116      */
    117     /*package*/ static final int AUTHENTICATION_NONE = 0;
    118 
    119     /**
    120      * Authentication requested; no man-in-the-middle protection required.
    121      * @hide
    122      */
    123     /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
    124 
    125     /**
    126      * Authentication with man-in-the-middle protection requested.
    127      * @hide
    128      */
    129     /*package*/ static final int AUTHENTICATION_MITM = 2;
    130 
    131     /**
    132      * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
    133      */
    134     private final IBluetoothGattCallback mBluetoothGattCallback =
    135         new BluetoothGattCallbackWrapper() {
    136             /**
    137              * Application interface registered - app is ready to go
    138              * @hide
    139              */
    140             public void onClientRegistered(int status, int clientIf) {
    141                 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
    142                     + " clientIf=" + clientIf);
    143                 if (VDBG) {
    144                     synchronized(mStateLock) {
    145                         if (mConnState != CONN_STATE_CONNECTING) {
    146                             Log.e(TAG, "Bad connection state: " + mConnState);
    147                         }
    148                     }
    149                 }
    150                 mClientIf = clientIf;
    151                 if (status != GATT_SUCCESS) {
    152                     mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
    153                                                       BluetoothProfile.STATE_DISCONNECTED);
    154                     synchronized(mStateLock) {
    155                         mConnState = CONN_STATE_IDLE;
    156                     }
    157                     return;
    158                 }
    159                 try {
    160                     mService.clientConnect(mClientIf, mDevice.getAddress(),
    161                                            !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
    162                 } catch (RemoteException e) {
    163                     Log.e(TAG,"",e);
    164                 }
    165             }
    166 
    167             /**
    168              * Client connection state changed
    169              * @hide
    170              */
    171             public void onClientConnectionState(int status, int clientIf,
    172                                                 boolean connected, String address) {
    173                 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
    174                                  + " clientIf=" + clientIf + " device=" + address);
    175                 if (!address.equals(mDevice.getAddress())) {
    176                     return;
    177                 }
    178                 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
    179                                                BluetoothProfile.STATE_DISCONNECTED;
    180                 try {
    181                     mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
    182                 } catch (Exception ex) {
    183                     Log.w(TAG, "Unhandled exception in callback", ex);
    184                 }
    185 
    186                 synchronized(mStateLock) {
    187                     if (connected) {
    188                         mConnState = CONN_STATE_CONNECTED;
    189                     } else {
    190                         mConnState = CONN_STATE_IDLE;
    191                     }
    192                 }
    193 
    194                 synchronized(mDeviceBusy) {
    195                     mDeviceBusy = false;
    196                 }
    197             }
    198 
    199             /**
    200              * A new GATT service has been discovered.
    201              * The service is added to the internal list and the search
    202              * continues.
    203              * @hide
    204              */
    205             public void onGetService(String address, int srvcType,
    206                                      int srvcInstId, ParcelUuid srvcUuid) {
    207                 if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
    208                 if (!address.equals(mDevice.getAddress())) {
    209                     return;
    210                 }
    211                 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
    212                                                        srvcInstId, srvcType));
    213             }
    214 
    215             /**
    216              * An included service has been found durig GATT discovery.
    217              * The included service is added to the respective parent.
    218              * @hide
    219              */
    220             public void onGetIncludedService(String address, int srvcType,
    221                                              int srvcInstId, ParcelUuid srvcUuid,
    222                                              int inclSrvcType, int inclSrvcInstId,
    223                                              ParcelUuid inclSrvcUuid) {
    224                 if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
    225                     + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
    226 
    227                 if (!address.equals(mDevice.getAddress())) {
    228                     return;
    229                 }
    230                 BluetoothGattService service = getService(mDevice,
    231                         srvcUuid.getUuid(), srvcInstId, srvcType);
    232                 BluetoothGattService includedService = getService(mDevice,
    233                         inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
    234 
    235                 if (service != null && includedService != null) {
    236                     service.addIncludedService(includedService);
    237                 }
    238             }
    239 
    240             /**
    241              * A new GATT characteristic has been discovered.
    242              * Add the new characteristic to the relevant service and continue
    243              * the remote device inspection.
    244              * @hide
    245              */
    246             public void onGetCharacteristic(String address, int srvcType,
    247                              int srvcInstId, ParcelUuid srvcUuid,
    248                              int charInstId, ParcelUuid charUuid,
    249                              int charProps) {
    250                 if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
    251                                charUuid);
    252 
    253                 if (!address.equals(mDevice.getAddress())) {
    254                     return;
    255                 }
    256                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    257                                                           srvcInstId, srvcType);
    258                 if (service != null) {
    259                     service.addCharacteristic(new BluetoothGattCharacteristic(
    260                            service, charUuid.getUuid(), charInstId, charProps, 0));
    261                 }
    262             }
    263 
    264             /**
    265              * A new GATT descriptor has been discovered.
    266              * Finally, add the descriptor to the related characteristic.
    267              * This should conclude the remote device update.
    268              * @hide
    269              */
    270             public void onGetDescriptor(String address, int srvcType,
    271                              int srvcInstId, ParcelUuid srvcUuid,
    272                              int charInstId, ParcelUuid charUuid,
    273                              int descrInstId, ParcelUuid descUuid) {
    274                 if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
    275 
    276                 if (!address.equals(mDevice.getAddress())) {
    277                     return;
    278                 }
    279                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    280                                                           srvcInstId, srvcType);
    281                 if (service == null) return;
    282 
    283                 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
    284                     charUuid.getUuid(), charInstId);
    285                 if (characteristic == null) return;
    286 
    287                 characteristic.addDescriptor(new BluetoothGattDescriptor(
    288                     characteristic, descUuid.getUuid(), descrInstId, 0));
    289             }
    290 
    291             /**
    292              * Remote search has been completed.
    293              * The internal object structure should now reflect the state
    294              * of the remote device database. Let the application know that
    295              * we are done at this point.
    296              * @hide
    297              */
    298             public void onSearchComplete(String address, int status) {
    299                 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
    300                 if (!address.equals(mDevice.getAddress())) {
    301                     return;
    302                 }
    303                 try {
    304                     mCallback.onServicesDiscovered(BluetoothGatt.this, status);
    305                 } catch (Exception ex) {
    306                     Log.w(TAG, "Unhandled exception in callback", ex);
    307                 }
    308             }
    309 
    310             /**
    311              * Remote characteristic has been read.
    312              * Updates the internal value.
    313              * @hide
    314              */
    315             public void onCharacteristicRead(String address, int status, int srvcType,
    316                              int srvcInstId, ParcelUuid srvcUuid,
    317                              int charInstId, ParcelUuid charUuid, byte[] value) {
    318                 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
    319                             + " UUID=" + charUuid + " Status=" + status);
    320 
    321                 if (!address.equals(mDevice.getAddress())) {
    322                     return;
    323                 }
    324 
    325                 synchronized(mDeviceBusy) {
    326                     mDeviceBusy = false;
    327                 }
    328 
    329                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
    330                   || status == GATT_INSUFFICIENT_ENCRYPTION)
    331                   && mAuthRetry == false) {
    332                     try {
    333                         mAuthRetry = true;
    334                         mService.readCharacteristic(mClientIf, address,
    335                             srvcType, srvcInstId, srvcUuid,
    336                             charInstId, charUuid, AUTHENTICATION_MITM);
    337                         return;
    338                     } catch (RemoteException e) {
    339                         Log.e(TAG,"",e);
    340                     }
    341                 }
    342 
    343                 mAuthRetry = false;
    344 
    345                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    346                                                           srvcInstId, srvcType);
    347                 if (service == null) return;
    348 
    349                 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
    350                         charUuid.getUuid(), charInstId);
    351                 if (characteristic == null) return;
    352 
    353                 if (status == 0) characteristic.setValue(value);
    354 
    355                 try {
    356                     mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
    357                 } catch (Exception ex) {
    358                     Log.w(TAG, "Unhandled exception in callback", ex);
    359                 }
    360             }
    361 
    362             /**
    363              * Characteristic has been written to the remote device.
    364              * Let the app know how we did...
    365              * @hide
    366              */
    367             public void onCharacteristicWrite(String address, int status, int srvcType,
    368                              int srvcInstId, ParcelUuid srvcUuid,
    369                              int charInstId, ParcelUuid charUuid) {
    370                 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
    371                             + " UUID=" + charUuid + " Status=" + status);
    372 
    373                 if (!address.equals(mDevice.getAddress())) {
    374                     return;
    375                 }
    376 
    377                 synchronized(mDeviceBusy) {
    378                     mDeviceBusy = false;
    379                 }
    380 
    381                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    382                                                           srvcInstId, srvcType);
    383                 if (service == null) return;
    384 
    385                 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
    386                         charUuid.getUuid(), charInstId);
    387                 if (characteristic == null) return;
    388 
    389                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
    390                   || status == GATT_INSUFFICIENT_ENCRYPTION)
    391                   && mAuthRetry == false) {
    392                     try {
    393                         mAuthRetry = true;
    394                         mService.writeCharacteristic(mClientIf, address,
    395                             srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
    396                             characteristic.getWriteType(), AUTHENTICATION_MITM,
    397                             characteristic.getValue());
    398                         return;
    399                     } catch (RemoteException e) {
    400                         Log.e(TAG,"",e);
    401                     }
    402                 }
    403 
    404                 mAuthRetry = false;
    405 
    406                 try {
    407                     mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
    408                 } catch (Exception ex) {
    409                     Log.w(TAG, "Unhandled exception in callback", ex);
    410                 }
    411             }
    412 
    413             /**
    414              * Remote characteristic has been updated.
    415              * Updates the internal value.
    416              * @hide
    417              */
    418             public void onNotify(String address, int srvcType,
    419                              int srvcInstId, ParcelUuid srvcUuid,
    420                              int charInstId, ParcelUuid charUuid,
    421                              byte[] value) {
    422                 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
    423 
    424                 if (!address.equals(mDevice.getAddress())) {
    425                     return;
    426                 }
    427                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    428                                                           srvcInstId, srvcType);
    429                 if (service == null) return;
    430 
    431                 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
    432                         charUuid.getUuid(), charInstId);
    433                 if (characteristic == null) return;
    434 
    435                 characteristic.setValue(value);
    436 
    437                 try {
    438                     mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
    439                 } catch (Exception ex) {
    440                     Log.w(TAG, "Unhandled exception in callback", ex);
    441                 }
    442             }
    443 
    444             /**
    445              * Descriptor has been read.
    446              * @hide
    447              */
    448             public void onDescriptorRead(String address, int status, int srvcType,
    449                              int srvcInstId, ParcelUuid srvcUuid,
    450                              int charInstId, ParcelUuid charUuid,
    451                              int descrInstId, ParcelUuid descrUuid,
    452                              byte[] value) {
    453                 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
    454 
    455                 if (!address.equals(mDevice.getAddress())) {
    456                     return;
    457                 }
    458 
    459                 synchronized(mDeviceBusy) {
    460                     mDeviceBusy = false;
    461                 }
    462 
    463                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    464                                                           srvcInstId, srvcType);
    465                 if (service == null) return;
    466 
    467                 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
    468                         charUuid.getUuid(), charInstId);
    469                 if (characteristic == null) return;
    470 
    471                 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
    472                         descrUuid.getUuid(), descrInstId);
    473                 if (descriptor == null) return;
    474 
    475                 if (status == 0) descriptor.setValue(value);
    476 
    477                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
    478                   || status == GATT_INSUFFICIENT_ENCRYPTION)
    479                   && mAuthRetry == false) {
    480                     try {
    481                         mAuthRetry = true;
    482                         mService.readDescriptor(mClientIf, address,
    483                             srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
    484                             descrInstId, descrUuid, AUTHENTICATION_MITM);
    485                         return;
    486                     } catch (RemoteException e) {
    487                         Log.e(TAG,"",e);
    488                     }
    489                 }
    490 
    491                 mAuthRetry = true;
    492 
    493                 try {
    494                     mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
    495                 } catch (Exception ex) {
    496                     Log.w(TAG, "Unhandled exception in callback", ex);
    497                 }
    498             }
    499 
    500             /**
    501              * Descriptor write operation complete.
    502              * @hide
    503              */
    504             public void onDescriptorWrite(String address, int status, int srvcType,
    505                              int srvcInstId, ParcelUuid srvcUuid,
    506                              int charInstId, ParcelUuid charUuid,
    507                              int descrInstId, ParcelUuid descrUuid) {
    508                 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
    509 
    510                 if (!address.equals(mDevice.getAddress())) {
    511                     return;
    512                 }
    513 
    514                 synchronized(mDeviceBusy) {
    515                     mDeviceBusy = false;
    516                 }
    517 
    518                 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
    519                                                           srvcInstId, srvcType);
    520                 if (service == null) return;
    521 
    522                 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
    523                         charUuid.getUuid(), charInstId);
    524                 if (characteristic == null) return;
    525 
    526                 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
    527                         descrUuid.getUuid(), descrInstId);
    528                 if (descriptor == null) return;
    529 
    530                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
    531                   || status == GATT_INSUFFICIENT_ENCRYPTION)
    532                   && mAuthRetry == false) {
    533                     try {
    534                         mAuthRetry = true;
    535                         mService.writeDescriptor(mClientIf, address,
    536                             srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
    537                             descrInstId, descrUuid, characteristic.getWriteType(),
    538                             AUTHENTICATION_MITM, descriptor.getValue());
    539                         return;
    540                     } catch (RemoteException e) {
    541                         Log.e(TAG,"",e);
    542                     }
    543                 }
    544 
    545                 mAuthRetry = false;
    546 
    547                 try {
    548                     mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
    549                 } catch (Exception ex) {
    550                     Log.w(TAG, "Unhandled exception in callback", ex);
    551                 }
    552             }
    553 
    554             /**
    555              * Prepared write transaction completed (or aborted)
    556              * @hide
    557              */
    558             public void onExecuteWrite(String address, int status) {
    559                 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
    560                     + " status=" + status);
    561                 if (!address.equals(mDevice.getAddress())) {
    562                     return;
    563                 }
    564 
    565                 synchronized(mDeviceBusy) {
    566                     mDeviceBusy = false;
    567                 }
    568 
    569                 try {
    570                     mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
    571                 } catch (Exception ex) {
    572                     Log.w(TAG, "Unhandled exception in callback", ex);
    573                 }
    574             }
    575 
    576             /**
    577              * Remote device RSSI has been read
    578              * @hide
    579              */
    580             public void onReadRemoteRssi(String address, int rssi, int status) {
    581                 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
    582                             " rssi=" + rssi + " status=" + status);
    583                 if (!address.equals(mDevice.getAddress())) {
    584                     return;
    585                 }
    586                 try {
    587                     mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
    588                 } catch (Exception ex) {
    589                     Log.w(TAG, "Unhandled exception in callback", ex);
    590                 }
    591             }
    592 
    593             /**
    594              * Callback invoked when the MTU for a given connection changes
    595              * @hide
    596              */
    597             public void onConfigureMTU(String address, int mtu, int status) {
    598                 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
    599                             " mtu=" + mtu + " status=" + status);
    600                 if (!address.equals(mDevice.getAddress())) {
    601                     return;
    602                 }
    603                 try {
    604                     mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
    605                 } catch (Exception ex) {
    606                     Log.w(TAG, "Unhandled exception in callback", ex);
    607                 }
    608             }
    609         };
    610 
    611     /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
    612                                 int transport) {
    613         mContext = context;
    614         mService = iGatt;
    615         mDevice = device;
    616         mTransport = transport;
    617         mServices = new ArrayList<BluetoothGattService>();
    618 
    619         mConnState = CONN_STATE_IDLE;
    620     }
    621 
    622     /**
    623      * Close this Bluetooth GATT client.
    624      *
    625      * Application should call this method as early as possible after it is done with
    626      * this GATT client.
    627      */
    628     public void close() {
    629         if (DBG) Log.d(TAG, "close()");
    630 
    631         unregisterApp();
    632         mConnState = CONN_STATE_CLOSED;
    633     }
    634 
    635     /**
    636      * Returns a service by UUID, instance and type.
    637      * @hide
    638      */
    639     /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
    640                                                 int instanceId, int type) {
    641         for(BluetoothGattService svc : mServices) {
    642             if (svc.getDevice().equals(device) &&
    643                 svc.getType() == type &&
    644                 svc.getInstanceId() == instanceId &&
    645                 svc.getUuid().equals(uuid)) {
    646                 return svc;
    647             }
    648         }
    649         return null;
    650     }
    651 
    652 
    653     /**
    654      * Register an application callback to start using GATT.
    655      *
    656      * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
    657      * is used to notify success or failure if the function returns true.
    658      *
    659      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    660      *
    661      * @param callback GATT callback handler that will receive asynchronous callbacks.
    662      * @return If true, the callback will be called to notify success or failure,
    663      *         false on immediate error
    664      */
    665     private boolean registerApp(BluetoothGattCallback callback) {
    666         if (DBG) Log.d(TAG, "registerApp()");
    667         if (mService == null) return false;
    668 
    669         mCallback = callback;
    670         UUID uuid = UUID.randomUUID();
    671         if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
    672 
    673         try {
    674             mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
    675         } catch (RemoteException e) {
    676             Log.e(TAG,"",e);
    677             return false;
    678         }
    679 
    680         return true;
    681     }
    682 
    683     /**
    684      * Unregister the current application and callbacks.
    685      */
    686     private void unregisterApp() {
    687         if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
    688         if (mService == null || mClientIf == 0) return;
    689 
    690         try {
    691             mCallback = null;
    692             mService.unregisterClient(mClientIf);
    693             mClientIf = 0;
    694         } catch (RemoteException e) {
    695             Log.e(TAG,"",e);
    696         }
    697     }
    698 
    699     /**
    700      * Initiate a connection to a Bluetooth GATT capable device.
    701      *
    702      * <p>The connection may not be established right away, but will be
    703      * completed when the remote device is available. A
    704      * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
    705      * invoked when the connection state changes as a result of this function.
    706      *
    707      * <p>The autoConnect parameter determines whether to actively connect to
    708      * the remote device, or rather passively scan and finalize the connection
    709      * when the remote device is in range/available. Generally, the first ever
    710      * connection to a device should be direct (autoConnect set to false) and
    711      * subsequent connections to known devices should be invoked with the
    712      * autoConnect parameter set to true.
    713      *
    714      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    715      *
    716      * @param device Remote device to connect to
    717      * @param autoConnect Whether to directly connect to the remote device (false)
    718      *                    or to automatically connect as soon as the remote
    719      *                    device becomes available (true).
    720      * @return true, if the connection attempt was initiated successfully
    721      */
    722     /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
    723         if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
    724         synchronized(mStateLock) {
    725             if (mConnState != CONN_STATE_IDLE) {
    726                 throw new IllegalStateException("Not idle");
    727             }
    728             mConnState = CONN_STATE_CONNECTING;
    729         }
    730         if (!registerApp(callback)) {
    731             synchronized(mStateLock) {
    732                 mConnState = CONN_STATE_IDLE;
    733             }
    734             Log.e(TAG, "Failed to register callback");
    735             return false;
    736         }
    737 
    738         // the connection will continue after successful callback registration
    739         mAutoConnect = autoConnect;
    740         return true;
    741     }
    742 
    743     /**
    744      * Disconnects an established connection, or cancels a connection attempt
    745      * currently in progress.
    746      *
    747      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    748      */
    749     public void disconnect() {
    750         if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
    751         if (mService == null || mClientIf == 0) return;
    752 
    753         try {
    754             mService.clientDisconnect(mClientIf, mDevice.getAddress());
    755         } catch (RemoteException e) {
    756             Log.e(TAG,"",e);
    757         }
    758     }
    759 
    760     /**
    761      * Connect back to remote device.
    762      *
    763      * <p>This method is used to re-connect to a remote device after the
    764      * connection has been dropped. If the device is not in range, the
    765      * re-connection will be triggered once the device is back in range.
    766      *
    767      * @return true, if the connection attempt was initiated successfully
    768      */
    769     public boolean connect() {
    770         try {
    771             mService.clientConnect(mClientIf, mDevice.getAddress(),
    772                                    false, mTransport); // autoConnect is inverse of "isDirect"
    773             return true;
    774         } catch (RemoteException e) {
    775             Log.e(TAG,"",e);
    776             return false;
    777         }
    778     }
    779 
    780     /**
    781      * Return the remote bluetooth device this GATT client targets to
    782      *
    783      * @return remote bluetooth device
    784      */
    785     public BluetoothDevice getDevice() {
    786         return mDevice;
    787     }
    788 
    789     /**
    790      * Discovers services offered by a remote device as well as their
    791      * characteristics and descriptors.
    792      *
    793      * <p>This is an asynchronous operation. Once service discovery is completed,
    794      * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
    795      * triggered. If the discovery was successful, the remote services can be
    796      * retrieved using the {@link #getServices} function.
    797      *
    798      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    799      *
    800      * @return true, if the remote service discovery has been started
    801      */
    802     public boolean discoverServices() {
    803         if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
    804         if (mService == null || mClientIf == 0) return false;
    805 
    806         mServices.clear();
    807 
    808         try {
    809             mService.discoverServices(mClientIf, mDevice.getAddress());
    810         } catch (RemoteException e) {
    811             Log.e(TAG,"",e);
    812             return false;
    813         }
    814 
    815         return true;
    816     }
    817 
    818     /**
    819      * Returns a list of GATT services offered by the remote device.
    820      *
    821      * <p>This function requires that service discovery has been completed
    822      * for the given device.
    823      *
    824      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    825      *
    826      * @return List of services on the remote device. Returns an empty list
    827      *         if service discovery has not yet been performed.
    828      */
    829     public List<BluetoothGattService> getServices() {
    830         List<BluetoothGattService> result =
    831                 new ArrayList<BluetoothGattService>();
    832 
    833         for (BluetoothGattService service : mServices) {
    834             if (service.getDevice().equals(mDevice)) {
    835                 result.add(service);
    836             }
    837         }
    838 
    839         return result;
    840     }
    841 
    842     /**
    843      * Returns a {@link BluetoothGattService}, if the requested UUID is
    844      * supported by the remote device.
    845      *
    846      * <p>This function requires that service discovery has been completed
    847      * for the given device.
    848      *
    849      * <p>If multiple instances of the same service (as identified by UUID)
    850      * exist, the first instance of the service is returned.
    851      *
    852      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    853      *
    854      * @param uuid UUID of the requested service
    855      * @return BluetoothGattService if supported, or null if the requested
    856      *         service is not offered by the remote device.
    857      */
    858     public BluetoothGattService getService(UUID uuid) {
    859         for (BluetoothGattService service : mServices) {
    860             if (service.getDevice().equals(mDevice) &&
    861                 service.getUuid().equals(uuid)) {
    862                 return service;
    863             }
    864         }
    865 
    866         return null;
    867     }
    868 
    869     /**
    870      * Reads the requested characteristic from the associated remote device.
    871      *
    872      * <p>This is an asynchronous operation. The result of the read operation
    873      * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
    874      * callback.
    875      *
    876      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    877      *
    878      * @param characteristic Characteristic to read from the remote device
    879      * @return true, if the read operation was initiated successfully
    880      */
    881     public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
    882         if ((characteristic.getProperties() &
    883                 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
    884 
    885         if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
    886         if (mService == null || mClientIf == 0) return false;
    887 
    888         BluetoothGattService service = characteristic.getService();
    889         if (service == null) return false;
    890 
    891         BluetoothDevice device = service.getDevice();
    892         if (device == null) return false;
    893 
    894         synchronized(mDeviceBusy) {
    895             if (mDeviceBusy) return false;
    896             mDeviceBusy = true;
    897         }
    898 
    899         try {
    900             mService.readCharacteristic(mClientIf, device.getAddress(),
    901                 service.getType(), service.getInstanceId(),
    902                 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
    903                 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
    904         } catch (RemoteException e) {
    905             Log.e(TAG,"",e);
    906             mDeviceBusy = false;
    907             return false;
    908         }
    909 
    910         return true;
    911     }
    912 
    913     /**
    914      * Writes a given characteristic and its values to the associated remote device.
    915      *
    916      * <p>Once the write operation has been completed, the
    917      * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
    918      * reporting the result of the operation.
    919      *
    920      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    921      *
    922      * @param characteristic Characteristic to write on the remote device
    923      * @return true, if the write operation was initiated successfully
    924      */
    925     public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    926         if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
    927             && (characteristic.getProperties() &
    928                 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
    929 
    930         if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
    931         if (mService == null || mClientIf == 0) return false;
    932 
    933         BluetoothGattService service = characteristic.getService();
    934         if (service == null) return false;
    935 
    936         BluetoothDevice device = service.getDevice();
    937         if (device == null) return false;
    938 
    939         synchronized(mDeviceBusy) {
    940             if (mDeviceBusy) return false;
    941             mDeviceBusy = true;
    942         }
    943 
    944         try {
    945             mService.writeCharacteristic(mClientIf, device.getAddress(),
    946                 service.getType(), service.getInstanceId(),
    947                 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
    948                 new ParcelUuid(characteristic.getUuid()),
    949                 characteristic.getWriteType(), AUTHENTICATION_NONE,
    950                 characteristic.getValue());
    951         } catch (RemoteException e) {
    952             Log.e(TAG,"",e);
    953             mDeviceBusy = false;
    954             return false;
    955         }
    956 
    957         return true;
    958     }
    959 
    960     /**
    961      * Reads the value for a given descriptor from the associated remote device.
    962      *
    963      * <p>Once the read operation has been completed, the
    964      * {@link BluetoothGattCallback#onDescriptorRead} callback is
    965      * triggered, signaling the result of the operation.
    966      *
    967      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
    968      *
    969      * @param descriptor Descriptor value to read from the remote device
    970      * @return true, if the read operation was initiated successfully
    971      */
    972     public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
    973         if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
    974         if (mService == null || mClientIf == 0) return false;
    975 
    976         BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
    977         if (characteristic == null) return false;
    978 
    979         BluetoothGattService service = characteristic.getService();
    980         if (service == null) return false;
    981 
    982         BluetoothDevice device = service.getDevice();
    983         if (device == null) return false;
    984 
    985         synchronized(mDeviceBusy) {
    986             if (mDeviceBusy) return false;
    987             mDeviceBusy = true;
    988         }
    989 
    990         try {
    991             mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
    992                 service.getInstanceId(), new ParcelUuid(service.getUuid()),
    993                 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
    994                 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
    995                 AUTHENTICATION_NONE);
    996         } catch (RemoteException e) {
    997             Log.e(TAG,"",e);
    998             mDeviceBusy = false;
    999             return false;
   1000         }
   1001 
   1002         return true;
   1003     }
   1004 
   1005     /**
   1006      * Write the value of a given descriptor to the associated remote device.
   1007      *
   1008      * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
   1009      * triggered to report the result of the write operation.
   1010      *
   1011      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1012      *
   1013      * @param descriptor Descriptor to write to the associated remote device
   1014      * @return true, if the write operation was initiated successfully
   1015      */
   1016     public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
   1017         if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
   1018         if (mService == null || mClientIf == 0) return false;
   1019 
   1020         BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
   1021         if (characteristic == null) return false;
   1022 
   1023         BluetoothGattService service = characteristic.getService();
   1024         if (service == null) return false;
   1025 
   1026         BluetoothDevice device = service.getDevice();
   1027         if (device == null) return false;
   1028 
   1029         synchronized(mDeviceBusy) {
   1030             if (mDeviceBusy) return false;
   1031             mDeviceBusy = true;
   1032         }
   1033 
   1034         try {
   1035             mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
   1036                 service.getInstanceId(), new ParcelUuid(service.getUuid()),
   1037                 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
   1038                 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
   1039                 characteristic.getWriteType(), AUTHENTICATION_NONE,
   1040                 descriptor.getValue());
   1041         } catch (RemoteException e) {
   1042             Log.e(TAG,"",e);
   1043             mDeviceBusy = false;
   1044             return false;
   1045         }
   1046 
   1047         return true;
   1048     }
   1049 
   1050     /**
   1051      * Initiates a reliable write transaction for a given remote device.
   1052      *
   1053      * <p>Once a reliable write transaction has been initiated, all calls
   1054      * to {@link #writeCharacteristic} are sent to the remote device for
   1055      * verification and queued up for atomic execution. The application will
   1056      * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
   1057      * in response to every {@link #writeCharacteristic} call and is responsible
   1058      * for verifying if the value has been transmitted accurately.
   1059      *
   1060      * <p>After all characteristics have been queued up and verified,
   1061      * {@link #executeReliableWrite} will execute all writes. If a characteristic
   1062      * was not written correctly, calling {@link #abortReliableWrite} will
   1063      * cancel the current transaction without commiting any values on the
   1064      * remote device.
   1065      *
   1066      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1067      *
   1068      * @return true, if the reliable write transaction has been initiated
   1069      */
   1070     public boolean beginReliableWrite() {
   1071         if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
   1072         if (mService == null || mClientIf == 0) return false;
   1073 
   1074         try {
   1075             mService.beginReliableWrite(mClientIf, mDevice.getAddress());
   1076         } catch (RemoteException e) {
   1077             Log.e(TAG,"",e);
   1078             return false;
   1079         }
   1080 
   1081         return true;
   1082     }
   1083 
   1084     /**
   1085      * Executes a reliable write transaction for a given remote device.
   1086      *
   1087      * <p>This function will commit all queued up characteristic write
   1088      * operations for a given remote device.
   1089      *
   1090      * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
   1091      * invoked to indicate whether the transaction has been executed correctly.
   1092      *
   1093      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1094      *
   1095      * @return true, if the request to execute the transaction has been sent
   1096      */
   1097     public boolean executeReliableWrite() {
   1098         if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
   1099         if (mService == null || mClientIf == 0) return false;
   1100 
   1101         synchronized(mDeviceBusy) {
   1102             if (mDeviceBusy) return false;
   1103             mDeviceBusy = true;
   1104         }
   1105 
   1106         try {
   1107             mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
   1108         } catch (RemoteException e) {
   1109             Log.e(TAG,"",e);
   1110             mDeviceBusy = false;
   1111             return false;
   1112         }
   1113 
   1114         return true;
   1115     }
   1116 
   1117     /**
   1118      * Cancels a reliable write transaction for a given device.
   1119      *
   1120      * <p>Calling this function will discard all queued characteristic write
   1121      * operations for a given remote device.
   1122      *
   1123      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1124      */
   1125     public void abortReliableWrite() {
   1126         if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
   1127         if (mService == null || mClientIf == 0) return;
   1128 
   1129         try {
   1130             mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
   1131         } catch (RemoteException e) {
   1132             Log.e(TAG,"",e);
   1133         }
   1134     }
   1135 
   1136     /**
   1137      * @deprecated Use {@link #abortReliableWrite()}
   1138      */
   1139     public void abortReliableWrite(BluetoothDevice mDevice) {
   1140         abortReliableWrite();
   1141     }
   1142 
   1143     /**
   1144      * Enable or disable notifications/indications for a given characteristic.
   1145      *
   1146      * <p>Once notifications are enabled for a characteristic, a
   1147      * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
   1148      * triggered if the remote device indicates that the given characteristic
   1149      * has changed.
   1150      *
   1151      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1152      *
   1153      * @param characteristic The characteristic for which to enable notifications
   1154      * @param enable Set to true to enable notifications/indications
   1155      * @return true, if the requested notification status was set successfully
   1156      */
   1157     public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
   1158                                               boolean enable) {
   1159         if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
   1160                          + " enable: " + enable);
   1161         if (mService == null || mClientIf == 0) return false;
   1162 
   1163         BluetoothGattService service = characteristic.getService();
   1164         if (service == null) return false;
   1165 
   1166         BluetoothDevice device = service.getDevice();
   1167         if (device == null) return false;
   1168 
   1169         try {
   1170             mService.registerForNotification(mClientIf, device.getAddress(),
   1171                 service.getType(), service.getInstanceId(),
   1172                 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
   1173                 new ParcelUuid(characteristic.getUuid()),
   1174                 enable);
   1175         } catch (RemoteException e) {
   1176             Log.e(TAG,"",e);
   1177             return false;
   1178         }
   1179 
   1180         return true;
   1181     }
   1182 
   1183     /**
   1184      * Clears the internal cache and forces a refresh of the services from the
   1185      * remote device.
   1186      * @hide
   1187      */
   1188     public boolean refresh() {
   1189         if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
   1190         if (mService == null || mClientIf == 0) return false;
   1191 
   1192         try {
   1193             mService.refreshDevice(mClientIf, mDevice.getAddress());
   1194         } catch (RemoteException e) {
   1195             Log.e(TAG,"",e);
   1196             return false;
   1197         }
   1198 
   1199         return true;
   1200     }
   1201 
   1202     /**
   1203      * Read the RSSI for a connected remote device.
   1204      *
   1205      * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
   1206      * invoked when the RSSI value has been read.
   1207      *
   1208      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1209      *
   1210      * @return true, if the RSSI value has been requested successfully
   1211      */
   1212     public boolean readRemoteRssi() {
   1213         if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
   1214         if (mService == null || mClientIf == 0) return false;
   1215 
   1216         try {
   1217             mService.readRemoteRssi(mClientIf, mDevice.getAddress());
   1218         } catch (RemoteException e) {
   1219             Log.e(TAG,"",e);
   1220             return false;
   1221         }
   1222 
   1223         return true;
   1224     }
   1225 
   1226     /**
   1227      * Request an MTU size used for a given connection.
   1228      *
   1229      * <p>When performing a write request operation (write without response),
   1230      * the data sent is truncated to the MTU size. This function may be used
   1231      * to request a larger MTU size to be able to send more data at once.
   1232      *
   1233      * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
   1234      * whether this operation was successful.
   1235      *
   1236      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
   1237      *
   1238      * @return true, if the new MTU value has been requested successfully
   1239      */
   1240     public boolean requestMtu(int mtu) {
   1241         if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
   1242                             + " mtu: " + mtu);
   1243         if (mService == null || mClientIf == 0) return false;
   1244 
   1245         try {
   1246             mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
   1247         } catch (RemoteException e) {
   1248             Log.e(TAG,"",e);
   1249             return false;
   1250         }
   1251 
   1252         return true;
   1253     }
   1254 
   1255     /**
   1256      * Request a connection parameter update.
   1257      *
   1258      * <p>This function will send a connection parameter update request to the
   1259      * remote device.
   1260      *
   1261      * @param connectionPriority Request a specific connection priority. Must be one of
   1262      *          {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
   1263      *          {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
   1264      *          or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
   1265      * @throws IllegalArgumentException If the parameters are outside of their
   1266      *                                  specified range.
   1267      */
   1268     public boolean requestConnectionPriority(int connectionPriority) {
   1269         if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
   1270             connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
   1271             throw new IllegalArgumentException("connectionPriority not within valid range");
   1272         }
   1273 
   1274         if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
   1275         if (mService == null || mClientIf == 0) return false;
   1276 
   1277         try {
   1278             mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
   1279         } catch (RemoteException e) {
   1280             Log.e(TAG,"",e);
   1281             return false;
   1282         }
   1283 
   1284         return true;
   1285     }
   1286 
   1287     /**
   1288      * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
   1289      * with {@link BluetoothProfile#GATT} as argument
   1290      *
   1291      * @throws UnsupportedOperationException
   1292      */
   1293     @Override
   1294     public int getConnectionState(BluetoothDevice device) {
   1295         throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
   1296     }
   1297 
   1298     /**
   1299      * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
   1300      * with {@link BluetoothProfile#GATT} as argument
   1301      *
   1302      * @throws UnsupportedOperationException
   1303      */
   1304     @Override
   1305     public List<BluetoothDevice> getConnectedDevices() {
   1306         throw new UnsupportedOperationException
   1307             ("Use BluetoothManager#getConnectedDevices instead.");
   1308     }
   1309 
   1310     /**
   1311      * Not supported - please use
   1312      * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
   1313      * with {@link BluetoothProfile#GATT} as first argument
   1314      *
   1315      * @throws UnsupportedOperationException
   1316      */
   1317     @Override
   1318     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
   1319         throw new UnsupportedOperationException
   1320             ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
   1321     }
   1322 }
   1323