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