1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.bluetooth;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.RequiresNoPermission;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SuppressLint;
25 import android.bluetooth.BluetoothGattCharacteristic.WriteType;
26 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
27 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
28 import android.compat.annotation.UnsupportedAppUsage;
29 import android.content.AttributionSource;
30 import android.os.Build;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.ParcelUuid;
34 import android.os.RemoteException;
35 import android.util.Log;
36 
37 import com.android.bluetooth.flags.Flags;
38 
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.UUID;
44 
45 /**
46  * Public API for the Bluetooth GATT Profile.
47  *
48  * <p>This class provides Bluetooth GATT functionality to enable communication with Bluetooth Smart
49  * or Smart Ready devices.
50  *
51  * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} and call
52  * {@link BluetoothDevice#connectGatt} to get a instance of this class. GATT capable devices can be
53  * discovered using the Bluetooth device discovery or BLE scan process.
54  */
55 public final class BluetoothGatt implements BluetoothProfile {
56     private static final String TAG = "BluetoothGatt";
57     private static final boolean DBG = true;
58     private static final boolean VDBG = false;
59 
60     @UnsupportedAppUsage private IBluetoothGatt mService;
61     @UnsupportedAppUsage private volatile BluetoothGattCallback mCallback;
62     private Handler mHandler;
63     @UnsupportedAppUsage private int mClientIf;
64     private BluetoothDevice mDevice;
65     @UnsupportedAppUsage private boolean mAutoConnect;
66 
67     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
68     private int mAuthRetryState;
69 
70     private int mConnState;
71     private final Object mStateLock = new Object();
72     private final Object mDeviceBusyLock = new Object();
73 
74     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
75     private Boolean mDeviceBusy = false;
76 
77     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
78     private int mTransport;
79 
80     private int mPhy;
81     private boolean mOpportunistic;
82     private final AttributionSource mAttributionSource;
83 
84     private static final int AUTH_RETRY_STATE_IDLE = 0;
85     private static final int AUTH_RETRY_STATE_MITM = 2;
86 
87     private static final int CONN_STATE_IDLE = 0;
88     private static final int CONN_STATE_CONNECTING = 1;
89     private static final int CONN_STATE_CONNECTED = 2;
90     private static final int CONN_STATE_CLOSED = 4;
91 
92     private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
93     private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds
94     // Max length of an attribute value, defined in gatt_api.h
95     private static final int GATT_MAX_ATTR_LEN = 512;
96 
97     private List<BluetoothGattService> mServices;
98 
99     /** A GATT operation completed successfully */
100     public static final int GATT_SUCCESS = 0;
101 
102     /** GATT read operation is not permitted */
103     public static final int GATT_READ_NOT_PERMITTED = 0x2;
104 
105     /** GATT write operation is not permitted */
106     public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
107 
108     /** Insufficient authentication for a given operation */
109     public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
110 
111     /** The given request is not supported */
112     public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
113 
114     /** Insufficient encryption for a given operation */
115     public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
116 
117     /** A read or write operation was requested with an invalid offset */
118     public static final int GATT_INVALID_OFFSET = 0x7;
119 
120     /** Insufficient authorization for a given operation */
121     public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8;
122 
123     /** A write operation exceeds the maximum length of the attribute */
124     public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
125 
126     /** A remote device connection is congested. */
127     public static final int GATT_CONNECTION_CONGESTED = 0x8f;
128 
129     /**
130      * GATT connection timed out, likely due to the remote device being out of range or not
131      * advertising as connectable.
132      */
133     @FlaggedApi(Flags.FLAG_ENUMERATE_GATT_ERRORS)
134     public static final int GATT_CONNECTION_TIMEOUT = 0x93;
135 
136     /** A GATT operation failed, errors other than the above */
137     public static final int GATT_FAILURE = 0x101;
138 
139     /**
140      * Connection parameter update - Use the connection parameters recommended by the Bluetooth SIG.
141      * This is the default value if no connection parameter update is requested.
142      */
143     public static final int CONNECTION_PRIORITY_BALANCED = 0;
144 
145     /**
146      * Connection parameter update - Request a high priority, low latency connection. An application
147      * should only request high priority connection parameters to transfer large amounts of data
148      * over LE quickly. Once the transfer is complete, the application should request {@link
149      * BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce energy use.
150      */
151     public static final int CONNECTION_PRIORITY_HIGH = 1;
152 
153     /** Connection parameter update - Request low power, reduced data rate connection parameters. */
154     public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
155 
156     /**
157      * Connection parameter update - Request the priority preferred for Digital Car Key for a lower
158      * latency connection. This connection parameter will consume more power than {@link
159      * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, so it is recommended that apps do not use this
160      * unless it specifically fits their use case.
161      */
162     public static final int CONNECTION_PRIORITY_DCK = 3;
163 
164     /**
165      * Connection subrate request - Balanced.
166      *
167      * @hide
168      */
169     public static final int SUBRATE_REQUEST_MODE_BALANCED = 0;
170 
171     /**
172      * Connection subrate request - High.
173      *
174      * @hide
175      */
176     public static final int SUBRATE_REQUEST_MODE_HIGH = 1;
177 
178     /**
179      * Connection Subrate Request - Low Power.
180      *
181      * @hide
182      */
183     public static final int SUBRATE_REQUEST_MODE_LOW_POWER = 2;
184 
185     /** @hide */
186     @Retention(RetentionPolicy.SOURCE)
187     @IntDef(
188             prefix = {"SUBRATE_REQUEST_MODE"},
189             value = {
190                 SUBRATE_REQUEST_MODE_BALANCED,
191                 SUBRATE_REQUEST_MODE_HIGH,
192                 SUBRATE_REQUEST_MODE_LOW_POWER,
193             })
194     public @interface SubrateRequestMode {}
195 
196     /**
197      * No authentication required.
198      *
199      * @hide
200      */
201     /*package*/ static final int AUTHENTICATION_NONE = 0;
202 
203     /**
204      * Authentication requested; no person-in-the-middle protection required.
205      *
206      * @hide
207      */
208     /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
209 
210     /**
211      * Authentication with person-in-the-middle protection requested.
212      *
213      * @hide
214      */
215     /*package*/ static final int AUTHENTICATION_MITM = 2;
216 
217     /** Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */
218     @SuppressLint("AndroidFrameworkBluetoothPermission")
219     private final IBluetoothGattCallback mBluetoothGattCallback =
220             new IBluetoothGattCallback.Stub() {
221                 /**
222                  * Application interface registered - app is ready to go
223                  *
224                  * @hide
225                  */
226                 @Override
227                 @SuppressLint("AndroidFrameworkRequiresPermission")
228                 public void onClientRegistered(int status, int clientIf) {
229                     if (DBG) {
230                         Log.d(
231                                 TAG,
232                                 "onClientRegistered() -"
233                                         + (" status=" + status)
234                                         + (" clientIf=" + clientIf));
235                     }
236                     mClientIf = clientIf;
237                     synchronized (mStateLock) {
238                         if (mConnState == CONN_STATE_CLOSED) {
239                             if (DBG) {
240                                 Log.d(
241                                         TAG,
242                                         "Client registration completed after closed,"
243                                                 + " unregistering");
244                             }
245                             unregisterApp();
246                             return;
247                         }
248                         if (VDBG) {
249                             if (mConnState != CONN_STATE_CONNECTING) {
250                                 Log.e(TAG, "Bad connection state: " + mConnState);
251                             }
252                         }
253                     }
254                     if (status != GATT_SUCCESS) {
255                         runOrQueueCallback(
256                                 new Runnable() {
257                                     @Override
258                                     public void run() {
259                                         final BluetoothGattCallback callback = mCallback;
260                                         if (callback != null) {
261                                             callback.onConnectionStateChange(
262                                                     BluetoothGatt.this,
263                                                     GATT_FAILURE,
264                                                     BluetoothProfile.STATE_DISCONNECTED);
265                                         }
266                                     }
267                                 });
268 
269                         synchronized (mStateLock) {
270                             mConnState = CONN_STATE_IDLE;
271                         }
272                         return;
273                     }
274                     try {
275                         // autoConnect is inverse of "isDirect"
276                         mService.clientConnect(
277                                 mClientIf,
278                                 mDevice.getAddress(),
279                                 mDevice.getAddressType(),
280                                 !mAutoConnect,
281                                 mTransport,
282                                 mOpportunistic,
283                                 mPhy,
284                                 mAttributionSource);
285                     } catch (RemoteException e) {
286                         Log.e(TAG, "", e);
287                     }
288                 }
289 
290                 /**
291                  * Phy update callback
292                  *
293                  * @hide
294                  */
295                 @Override
296                 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
297                     if (DBG) {
298                         Log.d(
299                                 TAG,
300                                 "onPhyUpdate() -"
301                                         + (" status=" + status)
302                                         + (" address=" + address)
303                                         + (" txPhy=" + txPhy)
304                                         + (" rxPhy=" + rxPhy));
305                     }
306                     if (!address.equals(mDevice.getAddress())) {
307                         return;
308                     }
309 
310                     runOrQueueCallback(
311                             new Runnable() {
312                                 @Override
313                                 public void run() {
314                                     final BluetoothGattCallback callback = mCallback;
315                                     if (callback != null) {
316                                         callback.onPhyUpdate(
317                                                 BluetoothGatt.this, txPhy, rxPhy, status);
318                                     }
319                                 }
320                             });
321                 }
322 
323                 /**
324                  * Phy read callback
325                  *
326                  * @hide
327                  */
328                 @Override
329                 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
330                     if (DBG) {
331                         Log.d(
332                                 TAG,
333                                 "onPhyRead() -"
334                                         + (" status=" + status)
335                                         + (" address=" + address)
336                                         + (" txPhy=" + txPhy)
337                                         + (" rxPhy=" + rxPhy));
338                     }
339                     if (!address.equals(mDevice.getAddress())) {
340                         return;
341                     }
342 
343                     runOrQueueCallback(
344                             new Runnable() {
345                                 @Override
346                                 public void run() {
347                                     final BluetoothGattCallback callback = mCallback;
348                                     if (callback != null) {
349                                         callback.onPhyRead(
350                                                 BluetoothGatt.this, txPhy, rxPhy, status);
351                                     }
352                                 }
353                             });
354                 }
355 
356                 /**
357                  * Client connection state changed
358                  *
359                  * @hide
360                  */
361                 @Override
362                 public void onClientConnectionState(
363                         int status, int clientIf, boolean connected, String address) {
364                     if (DBG) {
365                         Log.d(
366                                 TAG,
367                                 "onClientConnectionState() -"
368                                         + (" status=" + status)
369                                         + (" clientIf=" + clientIf)
370                                         + (" connected=" + connected)
371                                         + (" device=" + address));
372                     }
373                     if (!address.equals(mDevice.getAddress())) {
374                         return;
375                     }
376                     int profileState =
377                             connected
378                                     ? BluetoothProfile.STATE_CONNECTED
379                                     : BluetoothProfile.STATE_DISCONNECTED;
380 
381                     runOrQueueCallback(
382                             new Runnable() {
383                                 @Override
384                                 public void run() {
385                                     final BluetoothGattCallback callback = mCallback;
386                                     if (callback != null) {
387                                         callback.onConnectionStateChange(
388                                                 BluetoothGatt.this, status, profileState);
389                                     }
390                                 }
391                             });
392 
393                     synchronized (mStateLock) {
394                         if (connected) {
395                             mConnState = CONN_STATE_CONNECTED;
396                         } else {
397                             mConnState = CONN_STATE_IDLE;
398                         }
399                     }
400 
401                     synchronized (mDeviceBusyLock) {
402                         mDeviceBusy = false;
403                     }
404                 }
405 
406                 /**
407                  * Remote search has been completed. The internal object structure should now
408                  * reflect the state of the remote device database. Let the application know that we
409                  * are done at this point.
410                  *
411                  * @hide
412                  */
413                 @Override
414                 public void onSearchComplete(
415                         String address, List<BluetoothGattService> services, int status) {
416                     if (DBG) {
417                         Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
418                     }
419                     if (!address.equals(mDevice.getAddress())) {
420                         return;
421                     }
422 
423                     for (BluetoothGattService s : services) {
424                         // services we receive don't have device set properly.
425                         s.setDevice(mDevice);
426                     }
427 
428                     mServices.addAll(services);
429 
430                     // Fix references to included services, as they doesn't point to right objects.
431                     for (BluetoothGattService fixedService : mServices) {
432                         ArrayList<BluetoothGattService> includedServices =
433                                 new ArrayList(fixedService.getIncludedServices());
434                         fixedService.getIncludedServices().clear();
435 
436                         for (BluetoothGattService brokenRef : includedServices) {
437                             BluetoothGattService includedService =
438                                     getService(
439                                             mDevice,
440                                             brokenRef.getUuid(),
441                                             brokenRef.getInstanceId());
442                             if (includedService != null) {
443                                 fixedService.addIncludedService(includedService);
444                             } else {
445                                 Log.e(TAG, "Broken GATT database: can't find included service.");
446                             }
447                         }
448                     }
449 
450                     runOrQueueCallback(
451                             new Runnable() {
452                                 @Override
453                                 public void run() {
454                                     final BluetoothGattCallback callback = mCallback;
455                                     if (callback != null) {
456                                         callback.onServicesDiscovered(BluetoothGatt.this, status);
457                                     }
458                                 }
459                             });
460                 }
461 
462                 /**
463                  * Remote characteristic has been read. Updates the internal value.
464                  *
465                  * @hide
466                  */
467                 @Override
468                 @SuppressLint("AndroidFrameworkRequiresPermission")
469                 public void onCharacteristicRead(
470                         String address, int status, int handle, byte[] value) {
471                     if (VDBG) {
472                         Log.d(
473                                 TAG,
474                                 "onCharacteristicRead() -"
475                                         + (" Device=" + address)
476                                         + (" handle=" + handle)
477                                         + (" Status=" + status));
478                     }
479 
480                     if (!address.equals(mDevice.getAddress())) {
481                         return;
482                     }
483 
484                     synchronized (mDeviceBusyLock) {
485                         mDeviceBusy = false;
486                     }
487 
488                     if ((status == GATT_INSUFFICIENT_AUTHENTICATION
489                                     || status == GATT_INSUFFICIENT_ENCRYPTION)
490                             && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
491                         try {
492                             final int authReq =
493                                     (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
494                                             ? AUTHENTICATION_NO_MITM
495                                             : AUTHENTICATION_MITM;
496                             mService.readCharacteristic(
497                                     mClientIf, address, handle, authReq, mAttributionSource);
498                             mAuthRetryState++;
499                             return;
500                         } catch (RemoteException e) {
501                             Log.e(TAG, "", e);
502                         }
503                     }
504 
505                     mAuthRetryState = AUTH_RETRY_STATE_IDLE;
506 
507                     BluetoothGattCharacteristic characteristic =
508                             getCharacteristicById(mDevice, handle);
509                     if (characteristic == null) {
510                         Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
511                         return;
512                     }
513 
514                     runOrQueueCallback(
515                             new Runnable() {
516                                 @Override
517                                 public void run() {
518                                     final BluetoothGattCallback callback = mCallback;
519                                     if (callback != null) {
520                                         if (status == 0) characteristic.setValue(value);
521                                         callback.onCharacteristicRead(
522                                                 BluetoothGatt.this, characteristic, value, status);
523                                     }
524                                 }
525                             });
526                 }
527 
528                 /**
529                  * Characteristic has been written to the remote device. Let the app know how we
530                  * did...
531                  *
532                  * @hide
533                  */
534                 @Override
535                 @SuppressLint("AndroidFrameworkRequiresPermission")
536                 public void onCharacteristicWrite(
537                         String address, int status, int handle, byte[] value) {
538                     if (VDBG) {
539                         Log.d(
540                                 TAG,
541                                 "onCharacteristicWrite() -"
542                                         + (" Device=" + address)
543                                         + (" handle=" + handle)
544                                         + (" Status=" + status));
545                     }
546 
547                     if (!address.equals(mDevice.getAddress())) {
548                         return;
549                     }
550 
551                     synchronized (mDeviceBusyLock) {
552                         mDeviceBusy = false;
553                     }
554 
555                     BluetoothGattCharacteristic characteristic =
556                             getCharacteristicById(mDevice, handle);
557                     if (characteristic == null) return;
558 
559                     if ((status == GATT_INSUFFICIENT_AUTHENTICATION
560                                     || status == GATT_INSUFFICIENT_ENCRYPTION)
561                             && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
562                         try {
563                             final int authReq =
564                                     (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
565                                             ? AUTHENTICATION_NO_MITM
566                                             : AUTHENTICATION_MITM;
567                             int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
568                             for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
569                                 requestStatus =
570                                         mService.writeCharacteristic(
571                                                 mClientIf,
572                                                 address,
573                                                 handle,
574                                                 characteristic.getWriteType(),
575                                                 authReq,
576                                                 value,
577                                                 mAttributionSource);
578                                 if (requestStatus
579                                         != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
580                                     break;
581                                 }
582                                 try {
583                                     Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
584                                 } catch (InterruptedException e) {
585                                     Log.e(TAG, "", e);
586                                 }
587                             }
588                             mAuthRetryState++;
589                             return;
590                         } catch (RemoteException e) {
591                             Log.e(TAG, "", e);
592                         }
593                     }
594 
595                     mAuthRetryState = AUTH_RETRY_STATE_IDLE;
596                     runOrQueueCallback(
597                             new Runnable() {
598                                 @Override
599                                 public void run() {
600                                     final BluetoothGattCallback callback = mCallback;
601                                     if (callback != null) {
602                                         callback.onCharacteristicWrite(
603                                                 BluetoothGatt.this, characteristic, status);
604                                     }
605                                 }
606                             });
607                 }
608 
609                 /**
610                  * Remote characteristic has been updated. Updates the internal value.
611                  *
612                  * @hide
613                  */
614                 @Override
615                 public void onNotify(String address, int handle, byte[] value) {
616                     if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
617 
618                     if (!address.equals(mDevice.getAddress())) {
619                         return;
620                     }
621 
622                     BluetoothGattCharacteristic characteristic =
623                             getCharacteristicById(mDevice, handle);
624                     if (characteristic == null) return;
625 
626                     runOrQueueCallback(
627                             new Runnable() {
628                                 @Override
629                                 public void run() {
630                                     final BluetoothGattCallback callback = mCallback;
631                                     if (callback != null) {
632                                         characteristic.setValue(value);
633                                         callback.onCharacteristicChanged(
634                                                 BluetoothGatt.this, characteristic, value);
635                                     }
636                                 }
637                             });
638                 }
639 
640                 /**
641                  * Descriptor has been read.
642                  *
643                  * @hide
644                  */
645                 @Override
646                 @SuppressLint("AndroidFrameworkRequiresPermission")
647                 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
648                     if (VDBG) {
649                         Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
650                     }
651 
652                     if (!address.equals(mDevice.getAddress())) {
653                         return;
654                     }
655 
656                     synchronized (mDeviceBusyLock) {
657                         mDeviceBusy = false;
658                     }
659 
660                     BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
661                     if (descriptor == null) return;
662 
663                     if ((status == GATT_INSUFFICIENT_AUTHENTICATION
664                                     || status == GATT_INSUFFICIENT_ENCRYPTION)
665                             && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
666                         try {
667                             final int authReq =
668                                     (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
669                                             ? AUTHENTICATION_NO_MITM
670                                             : AUTHENTICATION_MITM;
671                             mService.readDescriptor(
672                                     mClientIf, address, handle, authReq, mAttributionSource);
673                             mAuthRetryState++;
674                             return;
675                         } catch (RemoteException e) {
676                             Log.e(TAG, "", e);
677                         }
678                     }
679 
680                     mAuthRetryState = AUTH_RETRY_STATE_IDLE;
681 
682                     runOrQueueCallback(
683                             new Runnable() {
684                                 @Override
685                                 public void run() {
686                                     final BluetoothGattCallback callback = mCallback;
687                                     if (callback != null) {
688                                         if (status == 0) descriptor.setValue(value);
689                                         callback.onDescriptorRead(
690                                                 BluetoothGatt.this, descriptor, status, value);
691                                     }
692                                 }
693                             });
694                 }
695 
696                 /**
697                  * Descriptor write operation complete.
698                  *
699                  * @hide
700                  */
701                 @Override
702                 @SuppressLint("AndroidFrameworkRequiresPermission")
703                 public void onDescriptorWrite(
704                         String address, int status, int handle, byte[] value) {
705                     if (VDBG) {
706                         Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
707                     }
708 
709                     if (!address.equals(mDevice.getAddress())) {
710                         return;
711                     }
712 
713                     synchronized (mDeviceBusyLock) {
714                         mDeviceBusy = false;
715                     }
716 
717                     BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
718                     if (descriptor == null) return;
719 
720                     if ((status == GATT_INSUFFICIENT_AUTHENTICATION
721                                     || status == GATT_INSUFFICIENT_ENCRYPTION)
722                             && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
723                         try {
724                             final int authReq =
725                                     (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
726                                             ? AUTHENTICATION_NO_MITM
727                                             : AUTHENTICATION_MITM;
728                             mService.writeDescriptor(
729                                     mClientIf, address, handle, authReq, value, mAttributionSource);
730                             mAuthRetryState++;
731                             return;
732                         } catch (RemoteException e) {
733                             Log.e(TAG, "", e);
734                         }
735                     }
736 
737                     mAuthRetryState = AUTH_RETRY_STATE_IDLE;
738 
739                     runOrQueueCallback(
740                             new Runnable() {
741                                 @Override
742                                 public void run() {
743                                     final BluetoothGattCallback callback = mCallback;
744                                     if (callback != null) {
745                                         callback.onDescriptorWrite(
746                                                 BluetoothGatt.this, descriptor, status);
747                                     }
748                                 }
749                             });
750                 }
751 
752                 /**
753                  * Prepared write transaction completed (or aborted)
754                  *
755                  * @hide
756                  */
757                 @Override
758                 public void onExecuteWrite(String address, int status) {
759                     if (VDBG) {
760                         Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status);
761                     }
762                     if (!address.equals(mDevice.getAddress())) {
763                         return;
764                     }
765 
766                     synchronized (mDeviceBusyLock) {
767                         mDeviceBusy = false;
768                     }
769 
770                     runOrQueueCallback(
771                             new Runnable() {
772                                 @Override
773                                 public void run() {
774                                     final BluetoothGattCallback callback = mCallback;
775                                     if (callback != null) {
776                                         callback.onReliableWriteCompleted(
777                                                 BluetoothGatt.this, status);
778                                     }
779                                 }
780                             });
781                 }
782 
783                 /**
784                  * Remote device RSSI has been read
785                  *
786                  * @hide
787                  */
788                 @Override
789                 public void onReadRemoteRssi(String address, int rssi, int status) {
790                     if (VDBG) {
791                         Log.d(
792                                 TAG,
793                                 "onReadRemoteRssi() -"
794                                         + (" Device=" + address)
795                                         + (" rssi=" + rssi)
796                                         + (" status=" + status));
797                     }
798                     if (!address.equals(mDevice.getAddress())) {
799                         return;
800                     }
801                     runOrQueueCallback(
802                             new Runnable() {
803                                 @Override
804                                 public void run() {
805                                     final BluetoothGattCallback callback = mCallback;
806                                     if (callback != null) {
807                                         callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
808                                     }
809                                 }
810                             });
811                 }
812 
813                 /**
814                  * Callback invoked when the MTU for a given connection changes
815                  *
816                  * @hide
817                  */
818                 @Override
819                 public void onConfigureMTU(String address, int mtu, int status) {
820                     if (DBG) {
821                         Log.d(
822                                 TAG,
823                                 "onConfigureMTU() -"
824                                         + (" Device=" + address)
825                                         + (" mtu=" + mtu)
826                                         + (" status=" + status));
827                     }
828                     if (!address.equals(mDevice.getAddress())) {
829                         return;
830                     }
831 
832                     runOrQueueCallback(
833                             new Runnable() {
834                                 @Override
835                                 public void run() {
836                                     final BluetoothGattCallback callback = mCallback;
837                                     if (callback != null) {
838                                         callback.onMtuChanged(BluetoothGatt.this, mtu, status);
839                                     }
840                                 }
841                             });
842                 }
843 
844                 /**
845                  * Callback invoked when the given connection is updated
846                  *
847                  * @hide
848                  */
849                 @Override
850                 public void onConnectionUpdated(
851                         String address, int interval, int latency, int timeout, int status) {
852                     if (DBG) {
853                         Log.d(
854                                 TAG,
855                                 "onConnectionUpdated() -"
856                                         + (" Device=" + address)
857                                         + (" interval=" + interval)
858                                         + (" latency=" + latency)
859                                         + (" timeout=" + timeout)
860                                         + (" status=" + status));
861                     }
862                     if (!address.equals(mDevice.getAddress())) {
863                         return;
864                     }
865 
866                     runOrQueueCallback(
867                             new Runnable() {
868                                 @Override
869                                 public void run() {
870                                     final BluetoothGattCallback callback = mCallback;
871                                     if (callback != null) {
872                                         callback.onConnectionUpdated(
873                                                 BluetoothGatt.this,
874                                                 interval,
875                                                 latency,
876                                                 timeout,
877                                                 status);
878                                     }
879                                 }
880                             });
881                 }
882 
883                 /**
884                  * Callback invoked when service changed event is received
885                  *
886                  * @hide
887                  */
888                 @Override
889                 public void onServiceChanged(String address) {
890                     if (DBG) {
891                         Log.d(TAG, "onServiceChanged() - Device=" + address);
892                     }
893 
894                     if (!address.equals(mDevice.getAddress())) {
895                         return;
896                     }
897 
898                     runOrQueueCallback(
899                             new Runnable() {
900                                 @Override
901                                 public void run() {
902                                     final BluetoothGattCallback callback = mCallback;
903                                     if (callback != null) {
904                                         callback.onServiceChanged(BluetoothGatt.this);
905                                     }
906                                 }
907                             });
908                 }
909 
910                 /**
911                  * Callback invoked when the given connection's subrate is changed
912                  *
913                  * @hide
914                  */
915                 @Override
916                 public void onSubrateChange(
917                         String address,
918                         int subrateFactor,
919                         int latency,
920                         int contNum,
921                         int timeout,
922                         int status) {
923                     Log.d(
924                             TAG,
925                             "onSubrateChange() - "
926                                     + (" Device=" + address)
927                                     + (" subrateFactor=" + subrateFactor)
928                                     + (" latency=" + latency)
929                                     + (" contNum=" + contNum)
930                                     + (" timeout=" + timeout)
931                                     + (" status=" + status));
932 
933                     if (!address.equals(mDevice.getAddress())) {
934                         return;
935                     }
936 
937                     runOrQueueCallback(
938                             new Runnable() {
939                                 @Override
940                                 public void run() {
941                                     final BluetoothGattCallback callback = mCallback;
942                                     if (callback != null) {
943                                         callback.onSubrateChange(
944                                                 BluetoothGatt.this,
945                                                 subrateFactor,
946                                                 latency,
947                                                 contNum,
948                                                 timeout,
949                                                 status);
950                                     }
951                                 }
952                             });
953                 }
954             };
955 
BluetoothGatt( IBluetoothGatt iGatt, BluetoothDevice device, int transport, boolean opportunistic, int phy, AttributionSource attributionSource)956     /* package */ BluetoothGatt(
957             IBluetoothGatt iGatt,
958             BluetoothDevice device,
959             int transport,
960             boolean opportunistic,
961             int phy,
962             AttributionSource attributionSource) {
963         mService = iGatt;
964         mDevice = device;
965         mTransport = transport;
966         mPhy = phy;
967         mOpportunistic = opportunistic;
968         mAttributionSource = attributionSource;
969         mServices = new ArrayList<BluetoothGattService>();
970 
971         mConnState = CONN_STATE_IDLE;
972         mAuthRetryState = AUTH_RETRY_STATE_IDLE;
973     }
974 
975     /** @hide */
976     @Override
onServiceConnected(IBinder service)977     public void onServiceConnected(IBinder service) {}
978 
979     /** @hide */
980     @Override
onServiceDisconnected()981     public void onServiceDisconnected() {}
982 
983     /** @hide */
984     @Override
getAdapter()985     public BluetoothAdapter getAdapter() {
986         return null;
987     }
988 
989     /**
990      * Close this Bluetooth GATT client.
991      *
992      * <p>Application should call this method as early as possible after it is done with this GATT
993      * client.
994      */
995     @RequiresBluetoothConnectPermission
996     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
close()997     public void close() {
998         if (DBG) Log.d(TAG, "close()");
999 
1000         unregisterApp();
1001         mConnState = CONN_STATE_CLOSED;
1002         mAuthRetryState = AUTH_RETRY_STATE_IDLE;
1003     }
1004 
1005     /**
1006      * Returns a service by UUID, instance and type.
1007      *
1008      * @hide
1009      */
getService(BluetoothDevice device, UUID uuid, int instanceId)1010     /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, int instanceId) {
1011         for (BluetoothGattService svc : mServices) {
1012             if (svc.getDevice().equals(device)
1013                     && svc.getInstanceId() == instanceId
1014                     && svc.getUuid().equals(uuid)) {
1015                 return svc;
1016             }
1017         }
1018         return null;
1019     }
1020 
1021     /**
1022      * Returns a characteristic with id equal to instanceId.
1023      *
1024      * @hide
1025      */
getCharacteristicById( BluetoothDevice device, int instanceId)1026     /*package*/ BluetoothGattCharacteristic getCharacteristicById(
1027             BluetoothDevice device, int instanceId) {
1028         for (BluetoothGattService svc : mServices) {
1029             for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
1030                 if (charac.getInstanceId() == instanceId) {
1031                     return charac;
1032                 }
1033             }
1034         }
1035         return null;
1036     }
1037 
1038     /**
1039      * Returns a descriptor with id equal to instanceId.
1040      *
1041      * @hide
1042      */
getDescriptorById(BluetoothDevice device, int instanceId)1043     /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
1044         for (BluetoothGattService svc : mServices) {
1045             for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
1046                 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
1047                     if (desc.getInstanceId() == instanceId) {
1048                         return desc;
1049                     }
1050                 }
1051             }
1052         }
1053         return null;
1054     }
1055 
1056     /**
1057      * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
1058      * immediately if no Handler was provided.
1059      */
runOrQueueCallback(final Runnable cb)1060     private void runOrQueueCallback(final Runnable cb) {
1061         if (mHandler == null) {
1062             try {
1063                 cb.run();
1064             } catch (Exception ex) {
1065                 Log.w(TAG, "Unhandled exception in callback", ex);
1066             }
1067         } else {
1068             mHandler.post(cb);
1069         }
1070     }
1071 
1072     /**
1073      * Register an application callback to start using GATT.
1074      *
1075      * <p>This is an asynchronous call. If registration is successful, client connection will be
1076      * initiated.
1077      *
1078      * @param callback GATT callback handler that will receive asynchronous callbacks.
1079      * @return If true, the callback will be called to notify success or failure, false on immediate
1080      *     error
1081      */
1082     @RequiresLegacyBluetoothPermission
1083     @RequiresBluetoothConnectPermission
1084     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
registerApp(BluetoothGattCallback callback, Handler handler)1085     private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
1086         return registerApp(callback, handler, false);
1087     }
1088 
1089     /**
1090      * Register an application callback to start using GATT.
1091      *
1092      * <p>This is an asynchronous call. If registration is successful, client connection will be
1093      * initiated.
1094      *
1095      * @param callback GATT callback handler that will receive asynchronous callbacks.
1096      * @param eattSupport indicate to allow for eatt support
1097      * @return If true, the callback will be called to notify success or failure, false on immediate
1098      *     error
1099      * @hide
1100      */
1101     @RequiresLegacyBluetoothPermission
1102     @RequiresBluetoothConnectPermission
1103     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
registerApp( BluetoothGattCallback callback, Handler handler, boolean eattSupport)1104     private boolean registerApp(
1105             BluetoothGattCallback callback, Handler handler, boolean eattSupport) {
1106         if (DBG) Log.d(TAG, "registerApp()");
1107         if (mService == null) return false;
1108 
1109         mCallback = callback;
1110         mHandler = handler;
1111         UUID uuid = UUID.randomUUID();
1112         if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
1113 
1114         try {
1115             mService.registerClient(
1116                     new ParcelUuid(uuid), mBluetoothGattCallback, eattSupport, mAttributionSource);
1117         } catch (RemoteException e) {
1118             Log.e(TAG, "", e);
1119             return false;
1120         }
1121 
1122         return true;
1123     }
1124 
1125     /** Unregister the current application and callbacks. */
1126     @UnsupportedAppUsage
1127     @RequiresBluetoothConnectPermission
1128     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
unregisterApp()1129     private void unregisterApp() {
1130         if (mService == null || mClientIf == 0) return;
1131         if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
1132 
1133         try {
1134             mCallback = null;
1135             mService.unregisterClient(mClientIf, mAttributionSource);
1136             mClientIf = 0;
1137         } catch (RemoteException e) {
1138             Log.e(TAG, "", e);
1139         }
1140     }
1141 
1142     /**
1143      * Initiate a connection to a Bluetooth GATT capable device.
1144      *
1145      * <p>The connection may not be established right away, but will be completed when the remote
1146      * device is available. A {@link BluetoothGattCallback#onConnectionStateChange} callback will be
1147      * invoked when the connection state changes as a result of this function.
1148      *
1149      * <p>The autoConnect parameter determines whether to actively connect to the remote device, or
1150      * rather passively scan and finalize the connection when the remote device is in
1151      * range/available. Generally, the first ever connection to a device should be direct
1152      * (autoConnect set to false) and subsequent connections to known devices should be invoked with
1153      * the autoConnect parameter set to true.
1154      *
1155      * @param autoConnect Whether to directly connect to the remote device (false) or to
1156      *     automatically connect as soon as the remote device becomes available (true).
1157      * @return true, if the connection attempt was initiated successfully
1158      */
1159     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1160     @RequiresLegacyBluetoothPermission
1161     @RequiresBluetoothConnectPermission
1162     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connect( Boolean autoConnect, BluetoothGattCallback callback, Handler handler)1163     /*package*/ boolean connect(
1164             Boolean autoConnect, BluetoothGattCallback callback, Handler handler) {
1165         if (DBG) {
1166             Log.d(TAG, "connect() - device: " + mDevice + ", auto: " + autoConnect);
1167         }
1168         synchronized (mStateLock) {
1169             if (mConnState != CONN_STATE_IDLE) {
1170                 throw new IllegalStateException("Not idle");
1171             }
1172             mConnState = CONN_STATE_CONNECTING;
1173         }
1174 
1175         mAutoConnect = autoConnect;
1176 
1177         if (!registerApp(callback, handler)) {
1178             synchronized (mStateLock) {
1179                 mConnState = CONN_STATE_IDLE;
1180             }
1181             Log.e(TAG, "Failed to register callback");
1182             return false;
1183         }
1184 
1185         // The connection will continue in the onClientRegistered callback
1186         return true;
1187     }
1188 
1189     /**
1190      * Disconnects an established connection, or cancels a connection attempt currently in progress.
1191      */
1192     @RequiresLegacyBluetoothPermission
1193     @RequiresBluetoothConnectPermission
1194     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
disconnect()1195     public void disconnect() {
1196         if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice);
1197         if (mService == null || mClientIf == 0) return;
1198 
1199         try {
1200             mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource);
1201         } catch (RemoteException e) {
1202             Log.e(TAG, "", e);
1203         }
1204     }
1205 
1206     /**
1207      * Connect back to remote device.
1208      *
1209      * <p>This method is used to re-connect to a remote device after the connection has been
1210      * dropped. If the device is not in range, the re-connection will be triggered once the device
1211      * is back in range.
1212      *
1213      * @return true, if the connection attempt was initiated successfully
1214      */
1215     @RequiresBluetoothConnectPermission
1216     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
connect()1217     public boolean connect() {
1218         try {
1219             if (DBG) {
1220                 Log.d(TAG, "connect(void) - device: " + mDevice + ", auto=" + mAutoConnect);
1221             }
1222 
1223             // autoConnect is inverse of "isDirect"
1224             mService.clientConnect(
1225                     mClientIf,
1226                     mDevice.getAddress(),
1227                     mDevice.getAddressType(),
1228                     !mAutoConnect,
1229                     mTransport,
1230                     mOpportunistic,
1231                     mPhy,
1232                     mAttributionSource);
1233             return true;
1234         } catch (RemoteException e) {
1235             Log.e(TAG, "", e);
1236             return false;
1237         }
1238     }
1239 
1240     /**
1241      * Set the preferred connection PHY for this app. Please note that this is just a
1242      * recommendation, whether the PHY change will happen depends on other applications preferences,
1243      * local and remote controller capabilities. Controller can override these settings.
1244      *
1245      * <p>{@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
1246      * if no PHY change happens. It is also triggered when remote device updates the PHY.
1247      *
1248      * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
1249      *     BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
1250      *     BluetoothDevice#PHY_LE_CODED_MASK}.
1251      * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
1252      *     BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
1253      *     BluetoothDevice#PHY_LE_CODED_MASK}.
1254      * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
1255      *     of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2}
1256      *     or {@link BluetoothDevice#PHY_OPTION_S8}
1257      */
1258     @RequiresBluetoothConnectPermission
1259     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
setPreferredPhy(int txPhy, int rxPhy, int phyOptions)1260     public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
1261         try {
1262             mService.clientSetPreferredPhy(
1263                     mClientIf, mDevice.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource);
1264         } catch (RemoteException e) {
1265             Log.e(TAG, "", e);
1266         }
1267     }
1268 
1269     /**
1270      * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
1271      * in {@link BluetoothGattCallback#onPhyRead}
1272      */
1273     @RequiresBluetoothConnectPermission
1274     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
readPhy()1275     public void readPhy() {
1276         try {
1277             mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource);
1278         } catch (RemoteException e) {
1279             Log.e(TAG, "", e);
1280         }
1281     }
1282 
1283     /**
1284      * Return the remote bluetooth device this GATT client targets to
1285      *
1286      * @return remote bluetooth device
1287      */
1288     @RequiresNoPermission
getDevice()1289     public BluetoothDevice getDevice() {
1290         return mDevice;
1291     }
1292 
1293     /**
1294      * Discovers services offered by a remote device as well as their characteristics and
1295      * descriptors.
1296      *
1297      * <p>This is an asynchronous operation. Once service discovery is completed, the {@link
1298      * BluetoothGattCallback#onServicesDiscovered} callback is triggered. If the discovery was
1299      * successful, the remote services can be retrieved using the {@link #getServices} function.
1300      *
1301      * @return true, if the remote service discovery has been started
1302      */
1303     @RequiresLegacyBluetoothPermission
1304     @RequiresBluetoothConnectPermission
1305     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
discoverServices()1306     public boolean discoverServices() {
1307         if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice);
1308         if (mService == null || mClientIf == 0) return false;
1309 
1310         mServices.clear();
1311 
1312         try {
1313             mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource);
1314         } catch (RemoteException e) {
1315             Log.e(TAG, "", e);
1316             return false;
1317         }
1318 
1319         return true;
1320     }
1321 
1322     /**
1323      * Discovers a service by UUID. This is exposed only for passing PTS tests. It should never be
1324      * used by real applications. The service is not searched for characteristics and descriptors,
1325      * or returned in any callback.
1326      *
1327      * @return true, if the remote service discovery has been started
1328      * @hide
1329      */
1330     @RequiresLegacyBluetoothPermission
1331     @RequiresBluetoothConnectPermission
1332     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
discoverServiceByUuid(UUID uuid)1333     public boolean discoverServiceByUuid(UUID uuid) {
1334         if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice);
1335         if (mService == null || mClientIf == 0) return false;
1336 
1337         mServices.clear();
1338 
1339         try {
1340             mService.discoverServiceByUuid(
1341                     mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource);
1342         } catch (RemoteException e) {
1343             Log.e(TAG, "", e);
1344             return false;
1345         }
1346         return true;
1347     }
1348 
1349     /**
1350      * Returns a list of GATT services offered by the remote device.
1351      *
1352      * <p>This function requires that service discovery has been completed for the given device.
1353      *
1354      * @return List of services on the remote device. Returns an empty list if service discovery has
1355      *     not yet been performed.
1356      */
1357     @RequiresLegacyBluetoothPermission
1358     @RequiresNoPermission
getServices()1359     public List<BluetoothGattService> getServices() {
1360         List<BluetoothGattService> result = new ArrayList<BluetoothGattService>();
1361 
1362         for (BluetoothGattService service : mServices) {
1363             if (service.getDevice().equals(mDevice)) {
1364                 result.add(service);
1365             }
1366         }
1367 
1368         return result;
1369     }
1370 
1371     /**
1372      * Returns a {@link BluetoothGattService}, if the requested UUID is supported by the remote
1373      * device.
1374      *
1375      * <p>This function requires that service discovery has been completed for the given device.
1376      *
1377      * <p>If multiple instances of the same service (as identified by UUID) exist, the first
1378      * instance of the service is returned.
1379      *
1380      * @param uuid UUID of the requested service
1381      * @return BluetoothGattService if supported, or null if the requested service is not offered by
1382      *     the remote device.
1383      */
1384     @RequiresLegacyBluetoothPermission
1385     @RequiresNoPermission
getService(UUID uuid)1386     public BluetoothGattService getService(UUID uuid) {
1387         for (BluetoothGattService service : mServices) {
1388             if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
1389                 return service;
1390             }
1391         }
1392 
1393         return null;
1394     }
1395 
1396     /**
1397      * Reads the requested characteristic from the associated remote device.
1398      *
1399      * <p>This is an asynchronous operation. The result of the read operation is reported by the
1400      * {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, BluetoothGattCharacteristic,
1401      * byte[], int)} callback.
1402      *
1403      * @param characteristic Characteristic to read from the remote device
1404      * @return true, if the read operation was initiated successfully
1405      */
1406     @RequiresLegacyBluetoothPermission
1407     @RequiresBluetoothConnectPermission
1408     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
readCharacteristic(BluetoothGattCharacteristic characteristic)1409     public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
1410         if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
1411             return false;
1412         }
1413 
1414         if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
1415         if (mService == null || mClientIf == 0) return false;
1416 
1417         BluetoothGattService service = characteristic.getService();
1418         if (service == null) return false;
1419 
1420         BluetoothDevice device = service.getDevice();
1421         if (device == null) return false;
1422 
1423         synchronized (mDeviceBusyLock) {
1424             if (mDeviceBusy) return false;
1425             mDeviceBusy = true;
1426         }
1427 
1428         try {
1429             mService.readCharacteristic(
1430                     mClientIf,
1431                     device.getAddress(),
1432                     characteristic.getInstanceId(),
1433                     AUTHENTICATION_NONE,
1434                     mAttributionSource);
1435         } catch (RemoteException e) {
1436             Log.e(TAG, "", e);
1437             synchronized (mDeviceBusyLock) {
1438                 mDeviceBusy = false;
1439             }
1440             return false;
1441         }
1442 
1443         return true;
1444     }
1445 
1446     /**
1447      * Reads the characteristic using its UUID from the associated remote device.
1448      *
1449      * <p>This is an asynchronous operation. The result of the read operation is reported by the
1450      * {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, BluetoothGattCharacteristic,
1451      * byte[], int)} callback.
1452      *
1453      * @param uuid UUID of characteristic to read from the remote device
1454      * @return true, if the read operation was initiated successfully
1455      * @hide
1456      */
1457     @RequiresLegacyBluetoothPermission
1458     @RequiresBluetoothConnectPermission
1459     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle)1460     public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1461         if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1462         if (mService == null || mClientIf == 0) return false;
1463 
1464         synchronized (mDeviceBusyLock) {
1465             if (mDeviceBusy) return false;
1466             mDeviceBusy = true;
1467         }
1468 
1469         try {
1470             mService.readUsingCharacteristicUuid(
1471                     mClientIf,
1472                     mDevice.getAddress(),
1473                     new ParcelUuid(uuid),
1474                     startHandle,
1475                     endHandle,
1476                     AUTHENTICATION_NONE,
1477                     mAttributionSource);
1478         } catch (RemoteException e) {
1479             Log.e(TAG, "", e);
1480             synchronized (mDeviceBusyLock) {
1481                 mDeviceBusy = false;
1482             }
1483             return false;
1484         }
1485 
1486         return true;
1487     }
1488 
1489     /**
1490      * Writes a given characteristic and its values to the associated remote device.
1491      *
1492      * <p>Once the write operation has been completed, the {@link
1493      * BluetoothGattCallback#onCharacteristicWrite} callback is invoked, reporting the result of the
1494      * operation.
1495      *
1496      * @param characteristic Characteristic to write on the remote device
1497      * @return true, if the write operation was initiated successfully
1498      * @throws IllegalArgumentException if characteristic or its value are null
1499      * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
1500      *     int)} as this is not memory safe because it relies on a {@link
1501      *     BluetoothGattCharacteristic} object whose underlying fields are subject to change outside
1502      *     this method.
1503      */
1504     @Deprecated
1505     @RequiresLegacyBluetoothPermission
1506     @RequiresBluetoothConnectPermission
1507     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
writeCharacteristic(BluetoothGattCharacteristic characteristic)1508     public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1509         try {
1510             return writeCharacteristic(
1511                             characteristic,
1512                             characteristic.getValue(),
1513                             characteristic.getWriteType())
1514                     == BluetoothStatusCodes.SUCCESS;
1515         } catch (Exception e) {
1516             return false;
1517         }
1518     }
1519 
1520     /** @hide */
1521     @Retention(RetentionPolicy.SOURCE)
1522     @IntDef(
1523             value = {
1524                 BluetoothStatusCodes.SUCCESS,
1525                 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
1526                 BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
1527                 BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
1528                 BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
1529                 BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
1530                 BluetoothStatusCodes.ERROR_UNKNOWN
1531             })
1532     public @interface WriteOperationReturnValues {}
1533 
1534     /**
1535      * Writes a given characteristic and its values to the associated remote device.
1536      *
1537      * <p>Once the write operation has been completed, the {@link
1538      * BluetoothGattCallback#onCharacteristicWrite} callback is invoked, reporting the result of the
1539      * operation.
1540      *
1541      * @param characteristic Characteristic to write on the remote device
1542      * @return whether the characteristic was successfully written to
1543      * @throws IllegalArgumentException if characteristic or value are null
1544      */
1545     @RequiresBluetoothConnectPermission
1546     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1547     @WriteOperationReturnValues
writeCharacteristic( @onNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value, @WriteType int writeType)1548     public int writeCharacteristic(
1549             @NonNull BluetoothGattCharacteristic characteristic,
1550             @NonNull byte[] value,
1551             @WriteType int writeType) {
1552         if (characteristic == null) {
1553             throw new IllegalArgumentException("characteristic must not be null");
1554         }
1555         if (value == null) {
1556             throw new IllegalArgumentException("value must not be null");
1557         }
1558         if (value.length > GATT_MAX_ATTR_LEN) {
1559             throw new IllegalArgumentException(
1560                     "value should not be longer than max length of an attribute value");
1561         }
1562         if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
1563         if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
1564                 && (characteristic.getProperties()
1565                                 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)
1566                         == 0) {
1567             return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
1568         }
1569         if (mService == null || mClientIf == 0) {
1570             return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
1571         }
1572 
1573         BluetoothGattService service = characteristic.getService();
1574         if (service == null) {
1575             throw new IllegalArgumentException("Characteristic must have a non-null service");
1576         }
1577 
1578         BluetoothDevice device = service.getDevice();
1579         if (device == null) {
1580             throw new IllegalArgumentException("Service must have a non-null device");
1581         }
1582 
1583         synchronized (mDeviceBusyLock) {
1584             if (mDeviceBusy) {
1585                 return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
1586             }
1587             mDeviceBusy = true;
1588         }
1589 
1590         int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
1591         try {
1592             for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
1593                 requestStatus =
1594                         mService.writeCharacteristic(
1595                                 mClientIf,
1596                                 device.getAddress(),
1597                                 characteristic.getInstanceId(),
1598                                 writeType,
1599                                 AUTHENTICATION_NONE,
1600                                 value,
1601                                 mAttributionSource);
1602                 if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
1603                     break;
1604                 }
1605                 try {
1606                     Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
1607                 } catch (InterruptedException e) {
1608                     Log.e(TAG, "", e);
1609                 }
1610             }
1611         } catch (RemoteException e) {
1612             Log.e(TAG, "", e);
1613             synchronized (mDeviceBusyLock) {
1614                 mDeviceBusy = false;
1615             }
1616             throw e.rethrowAsRuntimeException();
1617         }
1618         if (Flags.gattFixDeviceBusy()) {
1619             if (requestStatus != BluetoothStatusCodes.SUCCESS) {
1620                 synchronized (mDeviceBusyLock) {
1621                     mDeviceBusy = false;
1622                 }
1623             }
1624         }
1625 
1626         return requestStatus;
1627     }
1628 
1629     /**
1630      * Reads the value for a given descriptor from the associated remote device.
1631      *
1632      * <p>Once the read operation has been completed, the {@link
1633      * BluetoothGattCallback#onDescriptorRead} callback is triggered, signaling the result of the
1634      * operation.
1635      *
1636      * @param descriptor Descriptor value to read from the remote device
1637      * @return true, if the read operation was initiated successfully
1638      */
1639     @RequiresLegacyBluetoothPermission
1640     @RequiresBluetoothConnectPermission
1641     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
readDescriptor(BluetoothGattDescriptor descriptor)1642     public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
1643         if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
1644         if (mService == null || mClientIf == 0) return false;
1645 
1646         BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1647         if (characteristic == null) return false;
1648 
1649         BluetoothGattService service = characteristic.getService();
1650         if (service == null) return false;
1651 
1652         BluetoothDevice device = service.getDevice();
1653         if (device == null) return false;
1654 
1655         synchronized (mDeviceBusyLock) {
1656             if (mDeviceBusy) return false;
1657             mDeviceBusy = true;
1658         }
1659 
1660         try {
1661             mService.readDescriptor(
1662                     mClientIf,
1663                     device.getAddress(),
1664                     descriptor.getInstanceId(),
1665                     AUTHENTICATION_NONE,
1666                     mAttributionSource);
1667         } catch (RemoteException e) {
1668             Log.e(TAG, "", e);
1669             synchronized (mDeviceBusyLock) {
1670                 mDeviceBusy = false;
1671             }
1672             return false;
1673         }
1674 
1675         return true;
1676     }
1677 
1678     /**
1679      * Write the value of a given descriptor to the associated remote device.
1680      *
1681      * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
1682      * result of the write operation.
1683      *
1684      * @param descriptor Descriptor to write to the associated remote device
1685      * @return true, if the write operation was initiated successfully
1686      * @throws IllegalArgumentException if descriptor or its value are null
1687      * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
1688      *     this is not memory safe because it relies on a {@link BluetoothGattDescriptor} object
1689      *     whose underlying fields are subject to change outside this method.
1690      */
1691     @Deprecated
1692     @RequiresLegacyBluetoothPermission
1693     @RequiresBluetoothConnectPermission
1694     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
writeDescriptor(BluetoothGattDescriptor descriptor)1695     public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1696         try {
1697             return writeDescriptor(descriptor, descriptor.getValue())
1698                     == BluetoothStatusCodes.SUCCESS;
1699         } catch (Exception e) {
1700             return false;
1701         }
1702     }
1703 
1704     /**
1705      * Write the value of a given descriptor to the associated remote device.
1706      *
1707      * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
1708      * result of the write operation.
1709      *
1710      * @param descriptor Descriptor to write to the associated remote device
1711      * @return true, if the write operation was initiated successfully
1712      * @throws IllegalArgumentException if descriptor or value are null
1713      */
1714     @RequiresBluetoothConnectPermission
1715     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
1716     @WriteOperationReturnValues
writeDescriptor(@onNull BluetoothGattDescriptor descriptor, @NonNull byte[] value)1717     public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor, @NonNull byte[] value) {
1718         if (descriptor == null) {
1719             throw new IllegalArgumentException("descriptor must not be null");
1720         }
1721         if (value == null) {
1722             throw new IllegalArgumentException("value must not be null");
1723         }
1724         if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1725         if (mService == null || mClientIf == 0) {
1726             return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
1727         }
1728 
1729         BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1730         if (characteristic == null) {
1731             throw new IllegalArgumentException("Descriptor must have a non-null characteristic");
1732         }
1733 
1734         BluetoothGattService service = characteristic.getService();
1735         if (service == null) {
1736             throw new IllegalArgumentException("Characteristic must have a non-null service");
1737         }
1738 
1739         BluetoothDevice device = service.getDevice();
1740         if (device == null) {
1741             throw new IllegalArgumentException("Service must have a non-null device");
1742         }
1743 
1744         synchronized (mDeviceBusyLock) {
1745             if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
1746             mDeviceBusy = true;
1747         }
1748 
1749         try {
1750             return mService.writeDescriptor(
1751                     mClientIf,
1752                     device.getAddress(),
1753                     descriptor.getInstanceId(),
1754                     AUTHENTICATION_NONE,
1755                     value,
1756                     mAttributionSource);
1757         } catch (RemoteException e) {
1758             Log.e(TAG, "", e);
1759             synchronized (mDeviceBusyLock) {
1760                 mDeviceBusy = false;
1761             }
1762             throw e.rethrowAsRuntimeException();
1763         }
1764     }
1765 
1766     /**
1767      * Initiates a reliable write transaction for a given remote device.
1768      *
1769      * <p>Once a reliable write transaction has been initiated, all calls to {@link
1770      * #writeCharacteristic} are sent to the remote device for verification and queued up for atomic
1771      * execution. The application will receive a {@link BluetoothGattCallback#onCharacteristicWrite}
1772      * callback in response to every {@link #writeCharacteristic(BluetoothGattCharacteristic,
1773      * byte[], int)} call and is responsible for verifying if the value has been transmitted
1774      * accurately.
1775      *
1776      * <p>After all characteristics have been queued up and verified, {@link #executeReliableWrite}
1777      * will execute all writes. If a characteristic was not written correctly, calling {@link
1778      * #abortReliableWrite} will cancel the current transaction without committing any values on the
1779      * remote device.
1780      *
1781      * @return true, if the reliable write transaction has been initiated
1782      */
1783     @RequiresLegacyBluetoothPermission
1784     @RequiresBluetoothConnectPermission
1785     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
beginReliableWrite()1786     public boolean beginReliableWrite() {
1787         if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice);
1788         if (mService == null || mClientIf == 0) return false;
1789 
1790         try {
1791             mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource);
1792         } catch (RemoteException e) {
1793             Log.e(TAG, "", e);
1794             return false;
1795         }
1796 
1797         return true;
1798     }
1799 
1800     /**
1801      * Executes a reliable write transaction for a given remote device.
1802      *
1803      * <p>This function will commit all queued up characteristic write operations for a given remote
1804      * device.
1805      *
1806      * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is invoked to indicate
1807      * whether the transaction has been executed correctly.
1808      *
1809      * @return true, if the request to execute the transaction has been sent
1810      */
1811     @RequiresLegacyBluetoothPermission
1812     @RequiresBluetoothConnectPermission
1813     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
executeReliableWrite()1814     public boolean executeReliableWrite() {
1815         if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice);
1816         if (mService == null || mClientIf == 0) return false;
1817 
1818         synchronized (mDeviceBusyLock) {
1819             if (mDeviceBusy) return false;
1820             mDeviceBusy = true;
1821         }
1822 
1823         try {
1824             mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
1825         } catch (RemoteException e) {
1826             Log.e(TAG, "", e);
1827             synchronized (mDeviceBusyLock) {
1828                 mDeviceBusy = false;
1829             }
1830             return false;
1831         }
1832 
1833         return true;
1834     }
1835 
1836     /**
1837      * Cancels a reliable write transaction for a given device.
1838      *
1839      * <p>Calling this function will discard all queued characteristic write operations for a given
1840      * remote device.
1841      */
1842     @RequiresLegacyBluetoothPermission
1843     @RequiresBluetoothConnectPermission
1844     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
abortReliableWrite()1845     public void abortReliableWrite() {
1846         if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice);
1847         if (mService == null || mClientIf == 0) return;
1848 
1849         try {
1850             mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource);
1851         } catch (RemoteException e) {
1852             Log.e(TAG, "", e);
1853         }
1854     }
1855 
1856     /**
1857      * @deprecated Use {@link #abortReliableWrite()}
1858      */
1859     @Deprecated
1860     @RequiresBluetoothConnectPermission
1861     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
abortReliableWrite(BluetoothDevice mDevice)1862     public void abortReliableWrite(BluetoothDevice mDevice) {
1863         abortReliableWrite();
1864     }
1865 
1866     /**
1867      * Enable or disable notifications/indications for a given characteristic.
1868      *
1869      * <p>Once notifications are enabled for a characteristic, a {@link
1870      * BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, BluetoothGattCharacteristic,
1871      * byte[])} callback will be triggered if the remote device indicates that the given
1872      * characteristic has changed.
1873      *
1874      * @param characteristic The characteristic for which to enable notifications
1875      * @param enable Set to true to enable notifications/indications
1876      * @return true, if the requested notification status was set successfully
1877      */
1878     @RequiresLegacyBluetoothPermission
1879     @RequiresBluetoothConnectPermission
1880     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
setCharacteristicNotification( BluetoothGattCharacteristic characteristic, boolean enable)1881     public boolean setCharacteristicNotification(
1882             BluetoothGattCharacteristic characteristic, boolean enable) {
1883         if (DBG) {
1884             Log.d(
1885                     TAG,
1886                     "setCharacteristicNotification() - uuid: "
1887                             + characteristic.getUuid()
1888                             + " enable: "
1889                             + enable);
1890         }
1891         if (mService == null || mClientIf == 0) return false;
1892 
1893         BluetoothGattService service = characteristic.getService();
1894         if (service == null) return false;
1895 
1896         BluetoothDevice device = service.getDevice();
1897         if (device == null) return false;
1898 
1899         try {
1900             mService.registerForNotification(
1901                     mClientIf,
1902                     device.getAddress(),
1903                     characteristic.getInstanceId(),
1904                     enable,
1905                     mAttributionSource);
1906         } catch (RemoteException e) {
1907             Log.e(TAG, "", e);
1908             return false;
1909         }
1910 
1911         return true;
1912     }
1913 
1914     /**
1915      * Clears the internal cache and forces a refresh of the services from the remote device.
1916      *
1917      * @hide
1918      */
1919     @UnsupportedAppUsage
1920     @RequiresBluetoothConnectPermission
1921     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
refresh()1922     public boolean refresh() {
1923         if (DBG) Log.d(TAG, "refresh() - device: " + mDevice);
1924         if (mService == null || mClientIf == 0) return false;
1925 
1926         try {
1927             mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource);
1928         } catch (RemoteException e) {
1929             Log.e(TAG, "", e);
1930             return false;
1931         }
1932 
1933         return true;
1934     }
1935 
1936     /**
1937      * Read the RSSI for a connected remote device.
1938      *
1939      * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be invoked when the RSSI
1940      * value has been read.
1941      *
1942      * @return true, if the RSSI value has been requested successfully
1943      */
1944     @RequiresLegacyBluetoothPermission
1945     @RequiresBluetoothConnectPermission
1946     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
readRemoteRssi()1947     public boolean readRemoteRssi() {
1948         if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice);
1949         if (mService == null || mClientIf == 0) return false;
1950 
1951         try {
1952             mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource);
1953         } catch (RemoteException e) {
1954             Log.e(TAG, "", e);
1955             return false;
1956         }
1957 
1958         return true;
1959     }
1960 
1961     /**
1962      * Request an MTU size used for a given connection. Please note that starting from Android 14,
1963      * the Android Bluetooth stack requests the BLE ATT MTU to 517 bytes when the first GATT client
1964      * requests an MTU, and disregards all subsequent MTU requests. Check out <a
1965      * href="{@docRoot}about/versions/14/behavior-changes-all#mtu-set-to-517">MTU is set to 517 for
1966      * the first GATT client requesting an MTU</a> for more information.
1967      *
1968      * <p>When performing a write request operation (write without response), the data sent is
1969      * truncated to the MTU size. This function may be used to request a larger MTU size to be able
1970      * to send more data at once.
1971      *
1972      * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate whether this operation
1973      * was successful.
1974      *
1975      * @return true, if the new MTU value has been requested successfully
1976      */
1977     @RequiresLegacyBluetoothPermission
1978     @RequiresBluetoothConnectPermission
1979     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
requestMtu(int mtu)1980     public boolean requestMtu(int mtu) {
1981         if (DBG) {
1982             Log.d(TAG, "configureMTU() - device: " + mDevice + " mtu: " + mtu);
1983         }
1984         if (mService == null || mClientIf == 0) return false;
1985 
1986         try {
1987             mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource);
1988         } catch (RemoteException e) {
1989             Log.e(TAG, "", e);
1990             return false;
1991         }
1992 
1993         return true;
1994     }
1995 
1996     /**
1997      * Request a connection parameter update.
1998      *
1999      * <p>This function will send a connection parameter update request to the remote device.
2000      *
2001      * @param connectionPriority Request a specific connection priority. Must be one of {@link
2002      *     BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link
2003      *     BluetoothGatt#CONNECTION_PRIORITY_HIGH} {@link
2004      *     BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}, or {@link
2005      *     BluetoothGatt#CONNECTION_PRIORITY_DCK}.
2006      * @throws IllegalArgumentException If the parameters are outside of their specified range.
2007      */
2008     @RequiresBluetoothConnectPermission
2009     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
requestConnectionPriority(int connectionPriority)2010     public boolean requestConnectionPriority(int connectionPriority) {
2011         if (connectionPriority < CONNECTION_PRIORITY_BALANCED
2012                 || connectionPriority > CONNECTION_PRIORITY_DCK) {
2013             throw new IllegalArgumentException("connectionPriority not within valid range");
2014         }
2015 
2016         if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
2017         if (mService == null || mClientIf == 0) return false;
2018 
2019         try {
2020             mService.connectionParameterUpdate(
2021                     mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource);
2022         } catch (RemoteException e) {
2023             Log.e(TAG, "", e);
2024             return false;
2025         }
2026 
2027         return true;
2028     }
2029 
2030     /**
2031      * Request an LE connection parameter update.
2032      *
2033      * <p>This function will send an LE connection parameters update request to the remote device.
2034      *
2035      * @return true, if the request is send to the Bluetooth stack.
2036      * @hide
2037      */
2038     @RequiresBluetoothConnectPermission
2039     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
requestLeConnectionUpdate( int minConnectionInterval, int maxConnectionInterval, int slaveLatency, int supervisionTimeout, int minConnectionEventLen, int maxConnectionEventLen)2040     public boolean requestLeConnectionUpdate(
2041             int minConnectionInterval,
2042             int maxConnectionInterval,
2043             int slaveLatency,
2044             int supervisionTimeout,
2045             int minConnectionEventLen,
2046             int maxConnectionEventLen) {
2047         if (DBG) {
2048             Log.d(
2049                     TAG,
2050                     "requestLeConnectionUpdate() - min=("
2051                             + minConnectionInterval
2052                             + ")"
2053                             + (1.25 * minConnectionInterval)
2054                             + "msec, max=("
2055                             + maxConnectionInterval
2056                             + ")"
2057                             + (1.25 * maxConnectionInterval)
2058                             + "msec, latency="
2059                             + slaveLatency
2060                             + ", timeout="
2061                             + supervisionTimeout
2062                             + "msec"
2063                             + ", min_ce="
2064                             + minConnectionEventLen
2065                             + ", max_ce="
2066                             + maxConnectionEventLen);
2067         }
2068         if (mService == null || mClientIf == 0) return false;
2069 
2070         try {
2071             mService.leConnectionUpdate(
2072                     mClientIf,
2073                     mDevice.getAddress(),
2074                     minConnectionInterval,
2075                     maxConnectionInterval,
2076                     slaveLatency,
2077                     supervisionTimeout,
2078                     minConnectionEventLen,
2079                     maxConnectionEventLen,
2080                     mAttributionSource);
2081         } catch (RemoteException e) {
2082             Log.e(TAG, "", e);
2083             return false;
2084         }
2085 
2086         return true;
2087     }
2088 
2089     /**
2090      * Request LE subrate mode.
2091      *
2092      * <p>This function will send a LE subrate request to the remote device.
2093      *
2094      * @param subrateMode Request a specific subrate mode.
2095      * @throws IllegalArgumentException If the parameters are outside of their specified range.
2096      * @return true, if the request is send to the Bluetooth stack.
2097      * @hide
2098      */
2099     @RequiresBluetoothConnectPermission
2100     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
requestSubrateMode(@ubrateRequestMode int subrateMode)2101     public boolean requestSubrateMode(@SubrateRequestMode int subrateMode) {
2102         if (subrateMode < SUBRATE_REQUEST_MODE_BALANCED
2103                 || subrateMode > SUBRATE_REQUEST_MODE_LOW_POWER) {
2104             throw new IllegalArgumentException("Subrate Mode not within valid range");
2105         }
2106 
2107         if (DBG) {
2108             Log.d(TAG, "requestsubrateMode() - subrateMode: " + subrateMode);
2109         }
2110         if (mService == null || mClientIf == 0) {
2111             return false;
2112         }
2113 
2114         try {
2115             mService.subrateModeRequest(
2116                     mClientIf, mDevice.getAddress(), subrateMode, mAttributionSource);
2117         } catch (RemoteException e) {
2118             Log.e(TAG, "", e);
2119             return false;
2120         }
2121         return true;
2122     }
2123 
2124     /**
2125      * Request a LE subrate request.
2126      *
2127      * <p>This function will send a LE subrate request to the remote device.
2128      *
2129      * @return true, if the request is send to the Bluetooth stack.
2130      * @hide
2131      */
2132     @RequiresBluetoothConnectPermission
2133     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
bleSubrateRequest( int subrateMin, int subrateMax, int maxLatency, int contNumber, int supervisionTimeout)2134     public boolean bleSubrateRequest(
2135             int subrateMin,
2136             int subrateMax,
2137             int maxLatency,
2138             int contNumber,
2139             int supervisionTimeout) {
2140         if (DBG) {
2141             Log.d(
2142                     TAG,
2143                     "bleSubrateRequest() - subrateMin="
2144                             + subrateMin
2145                             + " subrateMax="
2146                             + (subrateMax)
2147                             + " maxLatency= "
2148                             + maxLatency
2149                             + "contNumber="
2150                             + contNumber
2151                             + " supervisionTimeout="
2152                             + supervisionTimeout);
2153         }
2154         if (mService == null || mClientIf == 0) {
2155             return false;
2156         }
2157 
2158         try {
2159             mService.leSubrateRequest(
2160                     mClientIf,
2161                     mDevice.getAddress(),
2162                     subrateMin,
2163                     subrateMax,
2164                     maxLatency,
2165                     contNumber,
2166                     supervisionTimeout,
2167                     mAttributionSource);
2168         } catch (RemoteException e) {
2169             Log.e(TAG, "", e);
2170             return false;
2171         }
2172         return true;
2173     }
2174 
2175     /**
2176      * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with
2177      *     {@link BluetoothProfile#GATT} as argument
2178      * @throws UnsupportedOperationException on every call
2179      */
2180     @Override
2181     @RequiresNoPermission
2182     @Deprecated
getConnectionState(BluetoothDevice device)2183     public int getConnectionState(BluetoothDevice device) {
2184         throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
2185     }
2186 
2187     /**
2188      * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with
2189      *     {@link BluetoothProfile#GATT} as argument
2190      * @throws UnsupportedOperationException on every call
2191      */
2192     @Override
2193     @RequiresNoPermission
2194     @Deprecated
getConnectedDevices()2195     public List<BluetoothDevice> getConnectedDevices() {
2196         throw new UnsupportedOperationException(
2197                 "Use BluetoothManager#getConnectedDevices instead.");
2198     }
2199 
2200     /**
2201      * @deprecated Not supported - please use {@link
2202      *     BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} with {@link
2203      *     BluetoothProfile#GATT} as first argument
2204      * @throws UnsupportedOperationException on every call
2205      */
2206     @Override
2207     @RequiresNoPermission
2208     @Deprecated
getDevicesMatchingConnectionStates(int[] states)2209     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
2210         throw new UnsupportedOperationException(
2211                 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
2212     }
2213 }
2214