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