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.compat.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 
BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport, boolean opportunistic, int phy)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      */
close()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      */
getService(BluetoothDevice device, UUID uuid, int instanceId)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      */
getCharacteristicById(BluetoothDevice device, int instanceId)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      */
getDescriptorById(BluetoothDevice device, int instanceId)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      */
runOrQueueCallback(final Runnable cb)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      */
registerApp(BluetoothGattCallback callback, Handler handler)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
unregisterApp()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
connect(Boolean autoConnect, BluetoothGattCallback callback, Handler handler)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      */
disconnect()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      */
connect()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      */
setPreferredPhy(int txPhy, int rxPhy, int phyOptions)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      */
readPhy()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      */
getDevice()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      */
discoverServices()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      */
discoverServiceByUuid(UUID uuid)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      */
getServices()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      */
getService(UUID uuid)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      */
readCharacteristic(BluetoothGattCharacteristic characteristic)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      */
readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle)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      */
writeCharacteristic(BluetoothGattCharacteristic characteristic)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      */
readDescriptor(BluetoothGattDescriptor descriptor)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      */
writeDescriptor(BluetoothGattDescriptor descriptor)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      */
beginReliableWrite()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      */
executeReliableWrite()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      */
abortReliableWrite()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
abortReliableWrite(BluetoothDevice mDevice)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      */
setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable)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
refresh()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      */
readRemoteRssi()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      */
requestMtu(int mtu)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      */
requestConnectionPriority(int connectionPriority)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      */
requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, int slaveLatency, int supervisionTimeout, int minConnectionEventLen, int maxConnectionEventLen)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
getConnectionState(BluetoothDevice device)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
getConnectedDevices()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
getDevicesMatchingConnectionStates(int[] states)1587     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1588         throw new UnsupportedOperationException(
1589                 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
1590     }
1591 }
1592