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