blob: f877f04626daae47055c918124014e27c593fb37 [file] [log] [blame]
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001/*
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
17package android.bluetooth;
18
Artur Satayeve23a0eb2019-12-10 17:47:52 +000019import android.compat.annotation.UnsupportedAppUsage;
Mathew Inwood8c854f82018-09-14 12:35:36 +010020import android.os.Build;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070021import android.os.Handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.os.ParcelUuid;
23import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080024import android.util.Log;
25
26import java.util.ArrayList;
27import java.util.List;
28import java.util.UUID;
29
30/**
Matthew Xieddf7e472013-03-01 18:41:02 -080031 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080032 *
Matthew Xieddf7e472013-03-01 18:41:02 -080033 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080034 * with Bluetooth Smart or Smart Ready devices.
35 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070036 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070037 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080038 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
39 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080040 */
41public final class BluetoothGatt implements BluetoothProfile {
42 private static final String TAG = "BluetoothGatt";
43 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070044 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045
Mathew Inwood7acad5e2018-08-01 15:00:35 +010046 @UnsupportedAppUsage
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080047 private IBluetoothGatt mService;
Mathew Inwood7acad5e2018-08-01 15:00:35 +010048 @UnsupportedAppUsage
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -070049 private volatile BluetoothGattCallback mCallback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070050 private Handler mHandler;
Mathew Inwood7acad5e2018-08-01 15:00:35 +010051 @UnsupportedAppUsage
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080052 private int mClientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -080053 private BluetoothDevice mDevice;
Mathew Inwood7acad5e2018-08-01 15:00:35 +010054 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -080055 private boolean mAutoConnect;
Mathew Inwood8c854f82018-09-14 12:35:36 +010056 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Jacky Cheung3854e222016-10-20 13:55:21 -070057 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080058 private int mConnState;
59 private final Object mStateLock = new Object();
Ajay Panicker6c96b702018-10-17 11:09:25 -070060 private final Object mDeviceBusyLock = new Object();
Mathew Inwood7acad5e2018-08-01 15:00:35 +010061 @UnsupportedAppUsage
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070062 private Boolean mDeviceBusy = false;
Mathew Inwood7acad5e2018-08-01 15:00:35 +010063 @UnsupportedAppUsage
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070064 private int mTransport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080065 private int mPhy;
Jack He13f52c82017-07-05 14:55:35 -070066 private boolean mOpportunistic;
Matthew Xieddf7e472013-03-01 18:41:02 -080067
Jacky Cheung3854e222016-10-20 13:55:21 -070068 private static final int AUTH_RETRY_STATE_IDLE = 0;
69 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
70 private static final int AUTH_RETRY_STATE_MITM = 2;
71
Matthew Xieddf7e472013-03-01 18:41:02 -080072 private static final int CONN_STATE_IDLE = 0;
73 private static final int CONN_STATE_CONNECTING = 1;
74 private static final int CONN_STATE_CONNECTED = 2;
75 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070076 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080077
78 private List<BluetoothGattService> mServices;
79
Matthew Xieddf7e472013-03-01 18:41:02 -080080 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080081 public static final int GATT_SUCCESS = 0;
82
Matthew Xieddf7e472013-03-01 18:41:02 -080083 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080084 public static final int GATT_READ_NOT_PERMITTED = 0x2;
85
Matthew Xieddf7e472013-03-01 18:41:02 -080086 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080087 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
88
89 /** Insufficient authentication for a given operation */
90 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
91
92 /** The given request is not supported */
93 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
94
95 /** Insufficient encryption for a given operation */
96 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
97
98 /** A read or write operation was requested with an invalid offset */
99 public static final int GATT_INVALID_OFFSET = 0x7;
100
101 /** A write operation exceeds the maximum length of the attribute */
102 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
103
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -0700104 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700105 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
106
Matthew Xie90ca8072013-05-28 21:06:50 +0000107 /** A GATT operation failed, errors other than the above */
108 public static final int GATT_FAILURE = 0x101;
109
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800110 /**
Stanley Tng505c0582018-05-03 08:50:49 -0700111 * Connection parameter update - Use the connection parameters recommended by the
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112 * Bluetooth SIG. This is the default value if no connection parameter update
113 * is requested.
114 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700115 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700116
117 /**
Stanley Tng505c0582018-05-03 08:50:49 -0700118 * Connection parameter update - Request a high priority, low latency connection.
119 * An application should only request high priority connection parameters to transfer large
120 * amounts of data over LE quickly. Once the transfer is complete, the application should
121 * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce
122 * energy use.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700123 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700124 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700125
Stanley Tng505c0582018-05-03 08:50:49 -0700126 /** Connection parameter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700127 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700128
129 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800130 * No authentication required.
Jack Hea355e5e2017-08-22 16:06:54 -0700131 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800132 * @hide
133 */
134 /*package*/ static final int AUTHENTICATION_NONE = 0;
135
136 /**
137 * Authentication requested; no man-in-the-middle protection required.
Jack Hea355e5e2017-08-22 16:06:54 -0700138 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800139 * @hide
140 */
141 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
142
143 /**
144 * Authentication with man-in-the-middle protection requested.
Jack Hea355e5e2017-08-22 16:06:54 -0700145 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800146 * @hide
147 */
148 /*package*/ static final int AUTHENTICATION_MITM = 2;
149
150 /**
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700151 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800152 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -0700153 private final IBluetoothGattCallback mBluetoothGattCallback =
Jack Hea355e5e2017-08-22 16:06:54 -0700154 new IBluetoothGattCallback.Stub() {
155 /**
156 * Application interface registered - app is ready to go
157 * @hide
158 */
159 @Override
160 public void onClientRegistered(int status, int clientIf) {
161 if (DBG) {
162 Log.d(TAG, "onClientRegistered() - status=" + status
163 + " clientIf=" + clientIf);
164 }
165 if (VDBG) {
166 synchronized (mStateLock) {
167 if (mConnState != CONN_STATE_CONNECTING) {
168 Log.e(TAG, "Bad connection state: " + mConnState);
169 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800170 }
171 }
Jack Hea355e5e2017-08-22 16:06:54 -0700172 mClientIf = clientIf;
173 if (status != GATT_SUCCESS) {
174 runOrQueueCallback(new Runnable() {
175 @Override
176 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700177 final BluetoothGattCallback callback = mCallback;
178 if (callback != null) {
179 callback.onConnectionStateChange(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700180 GATT_FAILURE,
181 BluetoothProfile.STATE_DISCONNECTED);
182 }
183 }
184 });
185
186 synchronized (mStateLock) {
187 mConnState = CONN_STATE_IDLE;
188 }
189 return;
190 }
191 try {
192 mService.clientConnect(mClientIf, mDevice.getAddress(),
193 !mAutoConnect, mTransport, mOpportunistic,
194 mPhy); // autoConnect is inverse of "isDirect"
195 } catch (RemoteException e) {
196 Log.e(TAG, "", e);
197 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800198 }
Jack Hea355e5e2017-08-22 16:06:54 -0700199
200 /**
201 * Phy update callback
202 * @hide
203 */
204 @Override
205 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
206 if (DBG) {
207 Log.d(TAG, "onPhyUpdate() - status=" + status
208 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
209 }
210 if (!address.equals(mDevice.getAddress())) {
211 return;
212 }
213
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700214 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700215 @Override
216 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700217 final BluetoothGattCallback callback = mCallback;
218 if (callback != null) {
219 callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700220 }
221 }
222 });
223 }
224
225 /**
226 * Phy read callback
227 * @hide
228 */
229 @Override
230 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
231 if (DBG) {
232 Log.d(TAG, "onPhyRead() - status=" + status
233 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
234 }
235 if (!address.equals(mDevice.getAddress())) {
236 return;
237 }
238
239 runOrQueueCallback(new Runnable() {
240 @Override
241 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700242 final BluetoothGattCallback callback = mCallback;
243 if (callback != null) {
244 callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700245 }
246 }
247 });
248 }
249
250 /**
251 * Client connection state changed
252 * @hide
253 */
254 @Override
255 public void onClientConnectionState(int status, int clientIf,
256 boolean connected, String address) {
257 if (DBG) {
258 Log.d(TAG, "onClientConnectionState() - status=" + status
259 + " clientIf=" + clientIf + " device=" + address);
260 }
261 if (!address.equals(mDevice.getAddress())) {
262 return;
263 }
264 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
265 BluetoothProfile.STATE_DISCONNECTED;
266
267 runOrQueueCallback(new Runnable() {
268 @Override
269 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700270 final BluetoothGattCallback callback = mCallback;
271 if (callback != null) {
272 callback.onConnectionStateChange(BluetoothGatt.this, status,
Jack Hea355e5e2017-08-22 16:06:54 -0700273 profileState);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700274 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700275 }
276 });
277
Jack Hea355e5e2017-08-22 16:06:54 -0700278 synchronized (mStateLock) {
279 if (connected) {
280 mConnState = CONN_STATE_CONNECTED;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800281 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700282 mConnState = CONN_STATE_IDLE;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800283 }
284 }
Jack Hea355e5e2017-08-22 16:06:54 -0700285
Ajay Panicker6c96b702018-10-17 11:09:25 -0700286 synchronized (mDeviceBusyLock) {
Jack Hea355e5e2017-08-22 16:06:54 -0700287 mDeviceBusy = false;
288 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800289 }
290
Jack Hea355e5e2017-08-22 16:06:54 -0700291 /**
292 * Remote search has been completed.
293 * The internal object structure should now reflect the state
294 * of the remote device database. Let the application know that
295 * we are done at this point.
296 * @hide
297 */
298 @Override
299 public void onSearchComplete(String address, List<BluetoothGattService> services,
300 int status) {
301 if (DBG) {
302 Log.d(TAG,
303 "onSearchComplete() = Device=" + address + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700304 }
Jack Hea355e5e2017-08-22 16:06:54 -0700305 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800307 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800308
Jack Hea355e5e2017-08-22 16:06:54 -0700309 for (BluetoothGattService s : services) {
310 //services we receive don't have device set properly.
311 s.setDevice(mDevice);
312 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800313
Jack Hea355e5e2017-08-22 16:06:54 -0700314 mServices.addAll(services);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800315
Jack Hea355e5e2017-08-22 16:06:54 -0700316 // Fix references to included services, as they doesn't point to right objects.
317 for (BluetoothGattService fixedService : mServices) {
318 ArrayList<BluetoothGattService> includedServices =
319 new ArrayList(fixedService.getIncludedServices());
320 fixedService.getIncludedServices().clear();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800321
Jack Hea355e5e2017-08-22 16:06:54 -0700322 for (BluetoothGattService brokenRef : includedServices) {
323 BluetoothGattService includedService = getService(mDevice,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700324 brokenRef.getUuid(), brokenRef.getInstanceId());
Jack Hea355e5e2017-08-22 16:06:54 -0700325 if (includedService != null) {
326 fixedService.addIncludedService(includedService);
327 } else {
328 Log.e(TAG, "Broken GATT database: can't find included service.");
329 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700330 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700331 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800332
Jack Hea355e5e2017-08-22 16:06:54 -0700333 runOrQueueCallback(new Runnable() {
334 @Override
335 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700336 final BluetoothGattCallback callback = mCallback;
337 if (callback != null) {
338 callback.onServicesDiscovered(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700339 }
340 }
341 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800342 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700343
Jack Hea355e5e2017-08-22 16:06:54 -0700344 /**
345 * Remote characteristic has been read.
346 * Updates the internal value.
347 * @hide
348 */
349 @Override
350 public void onCharacteristicRead(String address, int status, int handle,
351 byte[] value) {
352 if (VDBG) {
353 Log.d(TAG, "onCharacteristicRead() - Device=" + address
354 + " handle=" + handle + " Status=" + status);
355 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700356
Jack Hea355e5e2017-08-22 16:06:54 -0700357 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800358 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800359 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800360
Ajay Panicker6c96b702018-10-17 11:09:25 -0700361 synchronized (mDeviceBusyLock) {
Jack Hea355e5e2017-08-22 16:06:54 -0700362 mDeviceBusy = false;
363 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800364
Jack Hea355e5e2017-08-22 16:06:54 -0700365 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
366 || status == GATT_INSUFFICIENT_ENCRYPTION)
367 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
368 try {
Jack He2992cd02017-08-22 21:21:23 -0700369 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
370 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700371 mService.readCharacteristic(mClientIf, address, handle, authReq);
372 mAuthRetryState++;
373 return;
374 } catch (RemoteException e) {
375 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700376 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700377 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800378
Jack Hea355e5e2017-08-22 16:06:54 -0700379 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800380
Jack Hea355e5e2017-08-22 16:06:54 -0700381 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
382 handle);
383 if (characteristic == null) {
384 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700385 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800386 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800387
Jack Hea355e5e2017-08-22 16:06:54 -0700388 runOrQueueCallback(new Runnable() {
389 @Override
390 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700391 final BluetoothGattCallback callback = mCallback;
392 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700393 if (status == 0) characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700394 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700395 status);
396 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700397 }
Jack Hea355e5e2017-08-22 16:06:54 -0700398 });
399 }
400
401 /**
402 * Characteristic has been written to the remote device.
403 * Let the app know how we did...
404 * @hide
405 */
406 @Override
407 public void onCharacteristicWrite(String address, int status, int handle) {
408 if (VDBG) {
409 Log.d(TAG, "onCharacteristicWrite() - Device=" + address
410 + " handle=" + handle + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700411 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800412
Jack Hea355e5e2017-08-22 16:06:54 -0700413 if (!address.equals(mDevice.getAddress())) {
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700414 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800415 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800416
Ajay Panicker6c96b702018-10-17 11:09:25 -0700417 synchronized (mDeviceBusyLock) {
Jack Hea355e5e2017-08-22 16:06:54 -0700418 mDeviceBusy = false;
419 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800420
Jack Hea355e5e2017-08-22 16:06:54 -0700421 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
422 handle);
423 if (characteristic == null) return;
424
425 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
426 || status == GATT_INSUFFICIENT_ENCRYPTION)
427 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
428 try {
Jack He2992cd02017-08-22 21:21:23 -0700429 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
430 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700431 mService.writeCharacteristic(mClientIf, address, handle,
432 characteristic.getWriteType(), authReq,
433 characteristic.getValue());
434 mAuthRetryState++;
435 return;
436 } catch (RemoteException e) {
437 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700438 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700439 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440
Jack Hea355e5e2017-08-22 16:06:54 -0700441 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
442
443 runOrQueueCallback(new Runnable() {
444 @Override
445 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700446 final BluetoothGattCallback callback = mCallback;
447 if (callback != null) {
448 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700449 status);
450 }
451 }
452 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800453 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700454
Jack Hea355e5e2017-08-22 16:06:54 -0700455 /**
456 * Remote characteristic has been updated.
457 * Updates the internal value.
458 * @hide
459 */
460 @Override
461 public void onNotify(String address, int handle, byte[] value) {
462 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
463
464 if (!address.equals(mDevice.getAddress())) {
465 return;
466 }
467
468 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
469 handle);
470 if (characteristic == null) return;
471
Jack Hea355e5e2017-08-22 16:06:54 -0700472 runOrQueueCallback(new Runnable() {
473 @Override
474 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700475 final BluetoothGattCallback callback = mCallback;
476 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700477 characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700478 callback.onCharacteristicChanged(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700479 characteristic);
480 }
481 }
482 });
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700483 }
484
Jack Hea355e5e2017-08-22 16:06:54 -0700485 /**
486 * Descriptor has been read.
487 * @hide
488 */
489 @Override
490 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
491 if (VDBG) {
492 Log.d(TAG,
493 "onDescriptorRead() - Device=" + address + " handle=" + handle);
494 }
495
496 if (!address.equals(mDevice.getAddress())) {
497 return;
498 }
499
Ajay Panicker6c96b702018-10-17 11:09:25 -0700500 synchronized (mDeviceBusyLock) {
Jack Hea355e5e2017-08-22 16:06:54 -0700501 mDeviceBusy = false;
502 }
503
504 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
505 if (descriptor == null) return;
506
Jack Hea355e5e2017-08-22 16:06:54 -0700507
508 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
509 || status == GATT_INSUFFICIENT_ENCRYPTION)
510 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
511 try {
Jack He2992cd02017-08-22 21:21:23 -0700512 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
513 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700514 mService.readDescriptor(mClientIf, address, handle, authReq);
515 mAuthRetryState++;
516 return;
517 } catch (RemoteException e) {
518 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700519 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700520 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800521
Jack Hea355e5e2017-08-22 16:06:54 -0700522 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
523
524 runOrQueueCallback(new Runnable() {
525 @Override
526 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700527 final BluetoothGattCallback callback = mCallback;
528 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700529 if (status == 0) descriptor.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700530 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700531 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700532 }
Jack Hea355e5e2017-08-22 16:06:54 -0700533 });
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700534 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700535
Jack Hea355e5e2017-08-22 16:06:54 -0700536 /**
537 * Descriptor write operation complete.
538 * @hide
539 */
540 @Override
541 public void onDescriptorWrite(String address, int status, int handle) {
542 if (VDBG) {
543 Log.d(TAG,
544 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
545 }
546
547 if (!address.equals(mDevice.getAddress())) {
548 return;
549 }
550
Ajay Panicker6c96b702018-10-17 11:09:25 -0700551 synchronized (mDeviceBusyLock) {
Jack Hea355e5e2017-08-22 16:06:54 -0700552 mDeviceBusy = false;
553 }
554
555 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
556 if (descriptor == null) return;
557
558 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
559 || status == GATT_INSUFFICIENT_ENCRYPTION)
560 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
561 try {
Jack He2992cd02017-08-22 21:21:23 -0700562 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
563 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700564 mService.writeDescriptor(mClientIf, address, handle,
565 authReq, descriptor.getValue());
566 mAuthRetryState++;
567 return;
568 } catch (RemoteException e) {
569 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700570 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700571 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700572
Jack Hea355e5e2017-08-22 16:06:54 -0700573 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
574
575 runOrQueueCallback(new Runnable() {
576 @Override
577 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700578 final BluetoothGattCallback callback = mCallback;
579 if (callback != null) {
580 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700581 }
582 }
583 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700584 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700585
Jack Hea355e5e2017-08-22 16:06:54 -0700586 /**
587 * Prepared write transaction completed (or aborted)
588 * @hide
589 */
590 @Override
591 public void onExecuteWrite(String address, int status) {
592 if (VDBG) {
593 Log.d(TAG, "onExecuteWrite() - Device=" + address
594 + " status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700595 }
Jack Hea355e5e2017-08-22 16:06:54 -0700596 if (!address.equals(mDevice.getAddress())) {
597 return;
598 }
599
Ajay Panicker6c96b702018-10-17 11:09:25 -0700600 synchronized (mDeviceBusyLock) {
Jack Hea355e5e2017-08-22 16:06:54 -0700601 mDeviceBusy = false;
602 }
603
604 runOrQueueCallback(new Runnable() {
605 @Override
606 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700607 final BluetoothGattCallback callback = mCallback;
608 if (callback != null) {
609 callback.onReliableWriteCompleted(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700610 }
611 }
612 });
613 }
614
615 /**
616 * Remote device RSSI has been read
617 * @hide
618 */
619 @Override
620 public void onReadRemoteRssi(String address, int rssi, int status) {
621 if (VDBG) {
Jack He2992cd02017-08-22 21:21:23 -0700622 Log.d(TAG, "onReadRemoteRssi() - Device=" + address
623 + " rssi=" + rssi + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700624 }
625 if (!address.equals(mDevice.getAddress())) {
626 return;
627 }
628 runOrQueueCallback(new Runnable() {
629 @Override
630 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700631 final BluetoothGattCallback callback = mCallback;
632 if (callback != null) {
633 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700634 }
635 }
636 });
637 }
638
639 /**
640 * Callback invoked when the MTU for a given connection changes
641 * @hide
642 */
643 @Override
644 public void onConfigureMTU(String address, int mtu, int status) {
645 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700646 Log.d(TAG, "onConfigureMTU() - Device=" + address
647 + " mtu=" + mtu + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700648 }
649 if (!address.equals(mDevice.getAddress())) {
650 return;
651 }
652
653 runOrQueueCallback(new Runnable() {
654 @Override
655 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700656 final BluetoothGattCallback callback = mCallback;
657 if (callback != null) {
658 callback.onMtuChanged(BluetoothGatt.this, mtu, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700659 }
660 }
661 });
662 }
663
664 /**
665 * Callback invoked when the given connection is updated
666 * @hide
667 */
668 @Override
669 public void onConnectionUpdated(String address, int interval, int latency,
670 int timeout, int status) {
671 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700672 Log.d(TAG, "onConnectionUpdated() - Device=" + address
673 + " interval=" + interval + " latency=" + latency
674 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700675 }
676 if (!address.equals(mDevice.getAddress())) {
677 return;
678 }
679
680 runOrQueueCallback(new Runnable() {
681 @Override
682 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700683 final BluetoothGattCallback callback = mCallback;
684 if (callback != null) {
685 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
Jack Hea355e5e2017-08-22 16:06:54 -0700686 timeout, status);
687 }
688 }
689 });
690 }
691 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800692
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700693 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700694 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800695 mService = iGatt;
696 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700697 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800698 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700699 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800700 mServices = new ArrayList<BluetoothGattService>();
701
Matthew Xieddf7e472013-03-01 18:41:02 -0800702 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700703 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800704 }
705
706 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700707 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700708 *
709 * Application should call this method as early as possible after it is done with
710 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800711 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700712 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800713 if (DBG) Log.d(TAG, "close()");
714
715 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700716 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700717 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800718 }
719
720 /**
721 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700722 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800723 * @hide
724 */
725 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700726 int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700727 for (BluetoothGattService svc : mServices) {
Jack He2992cd02017-08-22 21:21:23 -0700728 if (svc.getDevice().equals(device)
Jack He2992cd02017-08-22 21:21:23 -0700729 && svc.getInstanceId() == instanceId
730 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800731 return svc;
732 }
733 }
734 return null;
735 }
736
737
738 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700739 * Returns a characteristic with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700740 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700741 * @hide
742 */
Jack Hea355e5e2017-08-22 16:06:54 -0700743 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
744 int instanceId) {
745 for (BluetoothGattService svc : mServices) {
746 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
747 if (charac.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700748 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700749 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700750 }
751 }
752 return null;
753 }
754
755 /**
756 * Returns a descriptor with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700757 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700758 * @hide
759 */
760 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700761 for (BluetoothGattService svc : mServices) {
762 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
763 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
764 if (desc.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700765 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700766 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700767 }
768 }
769 }
770 return null;
771 }
772
773 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700774 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
775 * immediately if no Handler was provided.
776 */
777 private void runOrQueueCallback(final Runnable cb) {
778 if (mHandler == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700779 try {
780 cb.run();
781 } catch (Exception ex) {
782 Log.w(TAG, "Unhandled exception in callback", ex);
783 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700784 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700785 mHandler.post(cb);
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700786 }
787 }
788
789 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800790 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800791 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700792 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800793 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800794 *
795 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
796 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800797 * @param callback GATT callback handler that will receive asynchronous callbacks.
Jack Hea355e5e2017-08-22 16:06:54 -0700798 * @return If true, the callback will be called to notify success or failure, false on immediate
799 * error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800800 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700801 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800802 if (DBG) Log.d(TAG, "registerApp()");
803 if (mService == null) return false;
804
805 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700806 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800807 UUID uuid = UUID.randomUUID();
808 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
809
810 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700811 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800812 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700813 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800814 return false;
815 }
816
817 return true;
818 }
819
820 /**
821 * Unregister the current application and callbacks.
822 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +0100823 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -0800824 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
826 if (mService == null || mClientIf == 0) return;
827
828 try {
829 mCallback = null;
830 mService.unregisterClient(mClientIf);
831 mClientIf = 0;
832 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700833 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800834 }
835 }
836
837 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800838 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800839 *
840 * <p>The connection may not be established right away, but will be
841 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700842 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800843 * invoked when the connection state changes as a result of this function.
844 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700845 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800846 * the remote device, or rather passively scan and finalize the connection
847 * when the remote device is in range/available. Generally, the first ever
848 * connection to a device should be direct (autoConnect set to false) and
849 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800850 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800851 *
852 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
853 *
854 * @param device Remote device to connect to
Jack Hea355e5e2017-08-22 16:06:54 -0700855 * @param autoConnect Whether to directly connect to the remote device (false) or to
856 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800857 * @return true, if the connection attempt was initiated successfully
858 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +0100859 @UnsupportedAppUsage
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700860 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700861 Handler handler) {
862 if (DBG) {
863 Log.d(TAG,
864 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
865 }
866 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800867 if (mConnState != CONN_STATE_IDLE) {
868 throw new IllegalStateException("Not idle");
869 }
870 mConnState = CONN_STATE_CONNECTING;
871 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700872
873 mAutoConnect = autoConnect;
874
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700875 if (!registerApp(callback, handler)) {
Jack Hea355e5e2017-08-22 16:06:54 -0700876 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800877 mConnState = CONN_STATE_IDLE;
878 }
879 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800880 return false;
881 }
882
Sungki Kimd35167a2016-05-19 10:18:07 -0700883 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800884 return true;
885 }
886
887 /**
888 * Disconnects an established connection, or cancels a connection attempt
889 * currently in progress.
890 *
891 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800892 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800893 public void disconnect() {
894 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800895 if (mService == null || mClientIf == 0) return;
896
897 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800898 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800899 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700900 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800901 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700902 }
903
904 /**
905 * Connect back to remote device.
906 *
907 * <p>This method is used to re-connect to a remote device after the
908 * connection has been dropped. If the device is not in range, the
909 * re-connection will be triggered once the device is back in range.
910 *
911 * @return true, if the connection attempt was initiated successfully
912 */
913 public boolean connect() {
914 try {
Jack He13f52c82017-07-05 14:55:35 -0700915 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
916 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700917 return true;
918 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700919 Log.e(TAG, "", e);
Matthew Xie33ec9842013-04-03 00:29:27 -0700920 return false;
921 }
922 }
923
924 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800925 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskicf733cd2017-09-14 08:51:44 -0700926 * recommendation, whether the PHY change will happen depends on other applications preferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800927 * local and remote controller capabilities. Controller can override these settings.
928 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700929 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800930 * if no PHY change happens. It is also triggered when remote device updates the PHY.
931 *
Jack Hea355e5e2017-08-22 16:06:54 -0700932 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
933 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
934 * BluetoothDevice#PHY_LE_CODED_MASK}.
935 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
936 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
937 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800938 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700939 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
940 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800941 */
942 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
943 try {
944 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700945 phyOptions);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800946 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700947 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800948 }
949 }
950
951 /**
952 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700953 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800954 */
955 public void readPhy() {
956 try {
957 mService.clientReadPhy(mClientIf, mDevice.getAddress());
958 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700959 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800960 }
961 }
962
963 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700964 * Return the remote bluetooth device this GATT client targets to
965 *
966 * @return remote bluetooth device
967 */
968 public BluetoothDevice getDevice() {
969 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800970 }
971
972 /**
973 * Discovers services offered by a remote device as well as their
974 * characteristics and descriptors.
975 *
976 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700977 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800978 * triggered. If the discovery was successful, the remote services can be
979 * retrieved using the {@link #getServices} function.
980 *
981 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
982 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800983 * @return true, if the remote service discovery has been started
984 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800985 public boolean discoverServices() {
986 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800987 if (mService == null || mClientIf == 0) return false;
988
989 mServices.clear();
990
991 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800992 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800993 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700994 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800995 return false;
996 }
997
998 return true;
999 }
1000
1001 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -07001002 * Discovers a service by UUID. This is exposed only for passing PTS tests.
1003 * It should never be used by real applications. The service is not searched
1004 * for characteristics and descriptors, or returned in any callback.
1005 *
1006 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1007 *
1008 * @return true, if the remote service discovery has been started
1009 * @hide
1010 */
1011 public boolean discoverServiceByUuid(UUID uuid) {
1012 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
1013 if (mService == null || mClientIf == 0) return false;
1014
1015 mServices.clear();
1016
1017 try {
1018 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
1019 } catch (RemoteException e) {
1020 Log.e(TAG, "", e);
1021 return false;
1022 }
1023 return true;
1024 }
1025
1026 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001027 * Returns a list of GATT services offered by the remote device.
1028 *
1029 * <p>This function requires that service discovery has been completed
1030 * for the given device.
1031 *
1032 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1033 *
Jack Hea355e5e2017-08-22 16:06:54 -07001034 * @return List of services on the remote device. Returns an empty list if service discovery has
1035 * not yet been performed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001036 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001037 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001038 List<BluetoothGattService> result =
1039 new ArrayList<BluetoothGattService>();
1040
1041 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001042 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001043 result.add(service);
1044 }
1045 }
1046
1047 return result;
1048 }
1049
1050 /**
1051 * Returns a {@link BluetoothGattService}, if the requested UUID is
1052 * supported by the remote device.
1053 *
1054 * <p>This function requires that service discovery has been completed
1055 * for the given device.
1056 *
1057 * <p>If multiple instances of the same service (as identified by UUID)
1058 * exist, the first instance of the service is returned.
1059 *
1060 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1061 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001062 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -07001063 * @return BluetoothGattService if supported, or null if the requested service is not offered by
1064 * the remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001065 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001066 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001067 for (BluetoothGattService service : mServices) {
Jack He2992cd02017-08-22 21:21:23 -07001068 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001069 return service;
1070 }
1071 }
1072
1073 return null;
1074 }
1075
1076 /**
1077 * Reads the requested characteristic from the associated remote device.
1078 *
1079 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001080 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001081 * callback.
1082 *
1083 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1084 *
1085 * @param characteristic Characteristic to read from the remote device
1086 * @return true, if the read operation was initiated successfully
1087 */
1088 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
Jack He2992cd02017-08-22 21:21:23 -07001089 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001090 return false;
1091 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001092
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001093 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001094 if (mService == null || mClientIf == 0) return false;
1095
1096 BluetoothGattService service = characteristic.getService();
1097 if (service == null) return false;
1098
1099 BluetoothDevice device = service.getDevice();
1100 if (device == null) return false;
1101
Ajay Panicker6c96b702018-10-17 11:09:25 -07001102 synchronized (mDeviceBusyLock) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001103 if (mDeviceBusy) return false;
1104 mDeviceBusy = true;
1105 }
1106
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001107 try {
1108 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001109 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001110 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001111 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001112 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001113 return false;
1114 }
1115
1116 return true;
1117 }
1118
1119 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001120 * Reads the characteristic using its UUID from the associated remote device.
1121 *
1122 * <p>This is an asynchronous operation. The result of the read operation
1123 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1124 * callback.
1125 *
1126 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1127 *
1128 * @param uuid UUID of characteristic to read from the remote device
1129 * @return true, if the read operation was initiated successfully
1130 * @hide
1131 */
1132 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1133 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1134 if (mService == null || mClientIf == 0) return false;
1135
Ajay Panicker6c96b702018-10-17 11:09:25 -07001136 synchronized (mDeviceBusyLock) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001137 if (mDeviceBusy) return false;
1138 mDeviceBusy = true;
1139 }
1140
1141 try {
1142 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001143 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001144 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001145 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001146 mDeviceBusy = false;
1147 return false;
1148 }
1149
1150 return true;
1151 }
1152
1153
1154 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001155 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001156 *
1157 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001158 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001159 * reporting the result of the operation.
1160 *
1161 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1162 *
1163 * @param characteristic Characteristic to write on the remote device
1164 * @return true, if the write operation was initiated successfully
1165 */
1166 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1167 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack He2992cd02017-08-22 21:21:23 -07001168 && (characteristic.getProperties()
1169 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001170 return false;
1171 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001172
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001173 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001174 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001175
1176 BluetoothGattService service = characteristic.getService();
1177 if (service == null) return false;
1178
1179 BluetoothDevice device = service.getDevice();
1180 if (device == null) return false;
1181
Ajay Panicker6c96b702018-10-17 11:09:25 -07001182 synchronized (mDeviceBusyLock) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001183 if (mDeviceBusy) return false;
1184 mDeviceBusy = true;
1185 }
1186
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001187 try {
1188 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001189 characteristic.getInstanceId(), characteristic.getWriteType(),
1190 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001191 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001192 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001193 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001194 return false;
1195 }
1196
1197 return true;
1198 }
1199
1200 /**
1201 * Reads the value for a given descriptor from the associated remote device.
1202 *
1203 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001204 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001205 * triggered, signaling the result of the operation.
1206 *
1207 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1208 *
1209 * @param descriptor Descriptor value to read from the remote device
1210 * @return true, if the read operation was initiated successfully
1211 */
1212 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001213 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001214 if (mService == null || mClientIf == 0) return false;
1215
1216 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1217 if (characteristic == null) return false;
1218
1219 BluetoothGattService service = characteristic.getService();
1220 if (service == null) return false;
1221
1222 BluetoothDevice device = service.getDevice();
1223 if (device == null) return false;
1224
Ajay Panicker6c96b702018-10-17 11:09:25 -07001225 synchronized (mDeviceBusyLock) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001226 if (mDeviceBusy) return false;
1227 mDeviceBusy = true;
1228 }
1229
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001230 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001231 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001232 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001234 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001235 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001236 return false;
1237 }
1238
1239 return true;
1240 }
1241
1242 /**
1243 * Write the value of a given descriptor to the associated remote device.
1244 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001245 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001246 * triggered to report the result of the write operation.
1247 *
1248 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1249 *
1250 * @param descriptor Descriptor to write to the associated remote device
1251 * @return true, if the write operation was initiated successfully
1252 */
1253 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001254 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001255 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001256
1257 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1258 if (characteristic == null) return false;
1259
1260 BluetoothGattService service = characteristic.getService();
1261 if (service == null) return false;
1262
1263 BluetoothDevice device = service.getDevice();
1264 if (device == null) return false;
1265
Ajay Panicker6c96b702018-10-17 11:09:25 -07001266 synchronized (mDeviceBusyLock) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001267 if (mDeviceBusy) return false;
1268 mDeviceBusy = true;
1269 }
1270
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001271 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001272 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001273 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001274 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001275 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001276 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001277 return false;
1278 }
1279
1280 return true;
1281 }
1282
1283 /**
1284 * Initiates a reliable write transaction for a given remote device.
1285 *
1286 * <p>Once a reliable write transaction has been initiated, all calls
1287 * to {@link #writeCharacteristic} are sent to the remote device for
1288 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001289 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001290 * in response to every {@link #writeCharacteristic} call and is responsible
1291 * for verifying if the value has been transmitted accurately.
1292 *
1293 * <p>After all characteristics have been queued up and verified,
1294 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1295 * was not written correctly, calling {@link #abortReliableWrite} will
kopriva219f7dc2018-10-09 13:42:28 -07001296 * cancel the current transaction without committing any values on the
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001297 * remote device.
1298 *
1299 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1300 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001301 * @return true, if the reliable write transaction has been initiated
1302 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001303 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001304 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001305 if (mService == null || mClientIf == 0) return false;
1306
1307 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001308 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001309 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001310 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001311 return false;
1312 }
1313
1314 return true;
1315 }
1316
1317 /**
1318 * Executes a reliable write transaction for a given remote device.
1319 *
1320 * <p>This function will commit all queued up characteristic write
1321 * operations for a given remote device.
1322 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001323 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001324 * invoked to indicate whether the transaction has been executed correctly.
1325 *
1326 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1327 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001328 * @return true, if the request to execute the transaction has been sent
1329 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001330 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001331 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001332 if (mService == null || mClientIf == 0) return false;
1333
Ajay Panicker6c96b702018-10-17 11:09:25 -07001334 synchronized (mDeviceBusyLock) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001335 if (mDeviceBusy) return false;
1336 mDeviceBusy = true;
1337 }
1338
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001339 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001340 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001341 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001342 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001343 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001344 return false;
1345 }
1346
1347 return true;
1348 }
1349
1350 /**
1351 * Cancels a reliable write transaction for a given device.
1352 *
1353 * <p>Calling this function will discard all queued characteristic write
1354 * operations for a given remote device.
1355 *
1356 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001357 */
John Du48f8b5d2013-08-19 12:20:37 -07001358 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001359 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001360 if (mService == null || mClientIf == 0) return;
1361
1362 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001363 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001364 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001365 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001366 }
1367 }
1368
1369 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001370 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001371 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001372 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001373 public void abortReliableWrite(BluetoothDevice mDevice) {
1374 abortReliableWrite();
1375 }
1376
1377 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001378 * Enable or disable notifications/indications for a given characteristic.
1379 *
1380 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001381 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001382 * triggered if the remote device indicates that the given characteristic
1383 * has changed.
1384 *
1385 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1386 *
1387 * @param characteristic The characteristic for which to enable notifications
1388 * @param enable Set to true to enable notifications/indications
1389 * @return true, if the requested notification status was set successfully
1390 */
1391 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001392 boolean enable) {
1393 if (DBG) {
1394 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1395 + " enable: " + enable);
1396 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001397 if (mService == null || mClientIf == 0) return false;
1398
1399 BluetoothGattService service = characteristic.getService();
1400 if (service == null) return false;
1401
1402 BluetoothDevice device = service.getDevice();
1403 if (device == null) return false;
1404
1405 try {
1406 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001407 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001408 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001409 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001410 return false;
1411 }
1412
1413 return true;
1414 }
1415
1416 /**
1417 * Clears the internal cache and forces a refresh of the services from the
1418 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001419 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001420 * @hide
1421 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +01001422 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -08001423 public boolean refresh() {
1424 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001425 if (mService == null || mClientIf == 0) return false;
1426
1427 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001428 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001429 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001430 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001431 return false;
1432 }
1433
1434 return true;
1435 }
1436
1437 /**
1438 * Read the RSSI for a connected remote device.
1439 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001440 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001441 * invoked when the RSSI value has been read.
1442 *
1443 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1444 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001445 * @return true, if the RSSI value has been requested successfully
1446 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001447 public boolean readRemoteRssi() {
1448 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001449 if (mService == null || mClientIf == 0) return false;
1450
1451 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001452 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001453 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001454 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001455 return false;
1456 }
1457
1458 return true;
1459 }
1460
1461 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001462 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001463 *
1464 * <p>When performing a write request operation (write without response),
1465 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001466 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001467 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001468 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001469 * whether this operation was successful.
1470 *
1471 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1472 *
1473 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001474 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001475 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001476 if (DBG) {
1477 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1478 + " mtu: " + mtu);
1479 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001480 if (mService == null || mClientIf == 0) return false;
1481
1482 try {
1483 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1484 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001485 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001486 return false;
1487 }
1488
1489 return true;
1490 }
1491
1492 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001493 * Request a connection parameter update.
1494 *
1495 * <p>This function will send a connection parameter update request to the
1496 * remote device.
1497 *
Jack Hea355e5e2017-08-22 16:06:54 -07001498 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1499 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1500 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1501 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001502 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001503 public boolean requestConnectionPriority(int connectionPriority) {
Jack He2992cd02017-08-22 21:21:23 -07001504 if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1505 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001506 throw new IllegalArgumentException("connectionPriority not within valid range");
1507 }
1508
Andre Eisenbach4072da02014-08-19 17:58:55 -07001509 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001510 if (mService == null || mClientIf == 0) return false;
1511
1512 try {
1513 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1514 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001515 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001516 return false;
1517 }
1518
1519 return true;
1520 }
1521
1522 /**
Stanley Tng6da1dda2018-01-04 15:42:25 -08001523 * Request an LE connection parameter update.
1524 *
1525 * <p>This function will send an LE connection parameters update request to the remote device.
1526 *
1527 * @return true, if the request is send to the Bluetooth stack.
1528 * @hide
1529 */
Stanley Tng148dd5b2018-03-19 12:28:56 -07001530 public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
1531 int slaveLatency, int supervisionTimeout,
1532 int minConnectionEventLen, int maxConnectionEventLen) {
Stanley Tng6da1dda2018-01-04 15:42:25 -08001533 if (DBG) {
1534 Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
Stanley Tng148dd5b2018-03-19 12:28:56 -07001535 + ")" + (1.25 * minConnectionInterval)
1536 + "msec, max=(" + maxConnectionInterval + ")"
Stanley Tng6da1dda2018-01-04 15:42:25 -08001537 + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
Stanley Tng148dd5b2018-03-19 12:28:56 -07001538 + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
1539 + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001540 }
1541 if (mService == null || mClientIf == 0) return false;
1542
1543 try {
1544 mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
Stanley Tng148dd5b2018-03-19 12:28:56 -07001545 minConnectionInterval, maxConnectionInterval,
1546 slaveLatency, supervisionTimeout,
1547 minConnectionEventLen, maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001548 } catch (RemoteException e) {
1549 Log.e(TAG, "", e);
1550 return false;
1551 }
1552
1553 return true;
1554 }
1555
1556 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001557 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1558 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001559 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001560 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001561 */
1562 @Override
1563 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001564 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001565 }
1566
1567 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001568 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1569 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001570 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001571 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001572 */
1573 @Override
1574 public List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -07001575 throw new UnsupportedOperationException(
1576 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001577 }
1578
1579 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001580 * Not supported - please use
1581 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1582 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001583 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001584 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001585 */
1586 @Override
1587 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -07001588 throw new UnsupportedOperationException(
1589 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001590 }
1591}