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