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