blob: 29d5a1c583a1b5ff2ee1406a607e1df2f881763d [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
Mathew Inwood7acad5e2018-08-01 15:00:35 +010019import android.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();
Mathew Inwood7acad5e2018-08-01 15:00:35 +010060 @UnsupportedAppUsage
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070061 private Boolean mDeviceBusy = false;
Mathew Inwood7acad5e2018-08-01 15:00:35 +010062 @UnsupportedAppUsage
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070063 private int mTransport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080064 private int mPhy;
Jack He13f52c82017-07-05 14:55:35 -070065 private boolean mOpportunistic;
Matthew Xieddf7e472013-03-01 18:41:02 -080066
Jacky Cheung3854e222016-10-20 13:55:21 -070067 private static final int AUTH_RETRY_STATE_IDLE = 0;
68 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
69 private static final int AUTH_RETRY_STATE_MITM = 2;
70
Matthew Xieddf7e472013-03-01 18:41:02 -080071 private static final int CONN_STATE_IDLE = 0;
72 private static final int CONN_STATE_CONNECTING = 1;
73 private static final int CONN_STATE_CONNECTED = 2;
74 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070075 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080076
77 private List<BluetoothGattService> mServices;
78
Matthew Xieddf7e472013-03-01 18:41:02 -080079 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080080 public static final int GATT_SUCCESS = 0;
81
Matthew Xieddf7e472013-03-01 18:41:02 -080082 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080083 public static final int GATT_READ_NOT_PERMITTED = 0x2;
84
Matthew Xieddf7e472013-03-01 18:41:02 -080085 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080086 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
87
88 /** Insufficient authentication for a given operation */
89 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
90
91 /** The given request is not supported */
92 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
93
94 /** Insufficient encryption for a given operation */
95 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
96
97 /** A read or write operation was requested with an invalid offset */
98 public static final int GATT_INVALID_OFFSET = 0x7;
99
100 /** A write operation exceeds the maximum length of the attribute */
101 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
102
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -0700103 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700104 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
105
Matthew Xie90ca8072013-05-28 21:06:50 +0000106 /** A GATT operation failed, errors other than the above */
107 public static final int GATT_FAILURE = 0x101;
108
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800109 /**
Stanley Tng505c0582018-05-03 08:50:49 -0700110 * Connection parameter update - Use the connection parameters recommended by the
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700111 * Bluetooth SIG. This is the default value if no connection parameter update
112 * is requested.
113 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700114 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700115
116 /**
Stanley Tng505c0582018-05-03 08:50:49 -0700117 * Connection parameter update - Request a high priority, low latency connection.
118 * An application should only request high priority connection parameters to transfer large
119 * amounts of data over LE quickly. Once the transfer is complete, the application should
120 * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce
121 * energy use.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700122 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700123 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700124
Stanley Tng505c0582018-05-03 08:50:49 -0700125 /** Connection parameter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700126 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700127
128 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800129 * No authentication required.
Jack Hea355e5e2017-08-22 16:06:54 -0700130 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800131 * @hide
132 */
133 /*package*/ static final int AUTHENTICATION_NONE = 0;
134
135 /**
136 * Authentication requested; no man-in-the-middle protection required.
Jack Hea355e5e2017-08-22 16:06:54 -0700137 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800138 * @hide
139 */
140 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
141
142 /**
143 * Authentication with man-in-the-middle protection requested.
Jack Hea355e5e2017-08-22 16:06:54 -0700144 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800145 * @hide
146 */
147 /*package*/ static final int AUTHENTICATION_MITM = 2;
148
149 /**
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700150 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800151 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -0700152 private final IBluetoothGattCallback mBluetoothGattCallback =
Jack Hea355e5e2017-08-22 16:06:54 -0700153 new IBluetoothGattCallback.Stub() {
154 /**
155 * Application interface registered - app is ready to go
156 * @hide
157 */
158 @Override
159 public void onClientRegistered(int status, int clientIf) {
160 if (DBG) {
161 Log.d(TAG, "onClientRegistered() - status=" + status
162 + " clientIf=" + clientIf);
163 }
164 if (VDBG) {
165 synchronized (mStateLock) {
166 if (mConnState != CONN_STATE_CONNECTING) {
167 Log.e(TAG, "Bad connection state: " + mConnState);
168 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800169 }
170 }
Jack Hea355e5e2017-08-22 16:06:54 -0700171 mClientIf = clientIf;
172 if (status != GATT_SUCCESS) {
173 runOrQueueCallback(new Runnable() {
174 @Override
175 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700176 final BluetoothGattCallback callback = mCallback;
177 if (callback != null) {
178 callback.onConnectionStateChange(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700179 GATT_FAILURE,
180 BluetoothProfile.STATE_DISCONNECTED);
181 }
182 }
183 });
184
185 synchronized (mStateLock) {
186 mConnState = CONN_STATE_IDLE;
187 }
188 return;
189 }
190 try {
191 mService.clientConnect(mClientIf, mDevice.getAddress(),
192 !mAutoConnect, mTransport, mOpportunistic,
193 mPhy); // autoConnect is inverse of "isDirect"
194 } catch (RemoteException e) {
195 Log.e(TAG, "", e);
196 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800197 }
Jack Hea355e5e2017-08-22 16:06:54 -0700198
199 /**
200 * Phy update callback
201 * @hide
202 */
203 @Override
204 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
205 if (DBG) {
206 Log.d(TAG, "onPhyUpdate() - status=" + status
207 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
208 }
209 if (!address.equals(mDevice.getAddress())) {
210 return;
211 }
212
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700213 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700214 @Override
215 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700216 final BluetoothGattCallback callback = mCallback;
217 if (callback != null) {
218 callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700219 }
220 }
221 });
222 }
223
224 /**
225 * Phy read callback
226 * @hide
227 */
228 @Override
229 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
230 if (DBG) {
231 Log.d(TAG, "onPhyRead() - status=" + status
232 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
233 }
234 if (!address.equals(mDevice.getAddress())) {
235 return;
236 }
237
238 runOrQueueCallback(new Runnable() {
239 @Override
240 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700241 final BluetoothGattCallback callback = mCallback;
242 if (callback != null) {
243 callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700244 }
245 }
246 });
247 }
248
249 /**
250 * Client connection state changed
251 * @hide
252 */
253 @Override
254 public void onClientConnectionState(int status, int clientIf,
255 boolean connected, String address) {
256 if (DBG) {
257 Log.d(TAG, "onClientConnectionState() - status=" + status
258 + " clientIf=" + clientIf + " device=" + address);
259 }
260 if (!address.equals(mDevice.getAddress())) {
261 return;
262 }
263 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
264 BluetoothProfile.STATE_DISCONNECTED;
265
266 runOrQueueCallback(new Runnable() {
267 @Override
268 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700269 final BluetoothGattCallback callback = mCallback;
270 if (callback != null) {
271 callback.onConnectionStateChange(BluetoothGatt.this, status,
Jack Hea355e5e2017-08-22 16:06:54 -0700272 profileState);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700273 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700274 }
275 });
276
Jack Hea355e5e2017-08-22 16:06:54 -0700277 synchronized (mStateLock) {
278 if (connected) {
279 mConnState = CONN_STATE_CONNECTED;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800280 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700281 mConnState = CONN_STATE_IDLE;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800282 }
283 }
Jack Hea355e5e2017-08-22 16:06:54 -0700284
285 synchronized (mDeviceBusy) {
286 mDeviceBusy = false;
287 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800288 }
289
Jack Hea355e5e2017-08-22 16:06:54 -0700290 /**
291 * Remote search has been completed.
292 * The internal object structure should now reflect the state
293 * of the remote device database. Let the application know that
294 * we are done at this point.
295 * @hide
296 */
297 @Override
298 public void onSearchComplete(String address, List<BluetoothGattService> services,
299 int status) {
300 if (DBG) {
301 Log.d(TAG,
302 "onSearchComplete() = Device=" + address + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700303 }
Jack Hea355e5e2017-08-22 16:06:54 -0700304 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800305 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800307
Jack Hea355e5e2017-08-22 16:06:54 -0700308 for (BluetoothGattService s : services) {
309 //services we receive don't have device set properly.
310 s.setDevice(mDevice);
311 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800312
Jack Hea355e5e2017-08-22 16:06:54 -0700313 mServices.addAll(services);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800314
Jack Hea355e5e2017-08-22 16:06:54 -0700315 // Fix references to included services, as they doesn't point to right objects.
316 for (BluetoothGattService fixedService : mServices) {
317 ArrayList<BluetoothGattService> includedServices =
318 new ArrayList(fixedService.getIncludedServices());
319 fixedService.getIncludedServices().clear();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800320
Jack Hea355e5e2017-08-22 16:06:54 -0700321 for (BluetoothGattService brokenRef : includedServices) {
322 BluetoothGattService includedService = getService(mDevice,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700323 brokenRef.getUuid(), brokenRef.getInstanceId());
Jack Hea355e5e2017-08-22 16:06:54 -0700324 if (includedService != null) {
325 fixedService.addIncludedService(includedService);
326 } else {
327 Log.e(TAG, "Broken GATT database: can't find included service.");
328 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700329 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700330 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800331
Jack Hea355e5e2017-08-22 16:06:54 -0700332 runOrQueueCallback(new Runnable() {
333 @Override
334 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700335 final BluetoothGattCallback callback = mCallback;
336 if (callback != null) {
337 callback.onServicesDiscovered(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700338 }
339 }
340 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800341 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700342
Jack Hea355e5e2017-08-22 16:06:54 -0700343 /**
344 * Remote characteristic has been read.
345 * Updates the internal value.
346 * @hide
347 */
348 @Override
349 public void onCharacteristicRead(String address, int status, int handle,
350 byte[] value) {
351 if (VDBG) {
352 Log.d(TAG, "onCharacteristicRead() - Device=" + address
353 + " handle=" + handle + " Status=" + status);
354 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700355
Jack Hea355e5e2017-08-22 16:06:54 -0700356 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800358 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800359
Jack Hea355e5e2017-08-22 16:06:54 -0700360 synchronized (mDeviceBusy) {
361 mDeviceBusy = false;
362 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800363
Jack Hea355e5e2017-08-22 16:06:54 -0700364 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
365 || status == GATT_INSUFFICIENT_ENCRYPTION)
366 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
367 try {
Jack He2992cd02017-08-22 21:21:23 -0700368 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
369 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700370 mService.readCharacteristic(mClientIf, address, handle, authReq);
371 mAuthRetryState++;
372 return;
373 } catch (RemoteException e) {
374 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700375 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700376 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800377
Jack Hea355e5e2017-08-22 16:06:54 -0700378 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800379
Jack Hea355e5e2017-08-22 16:06:54 -0700380 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
381 handle);
382 if (characteristic == null) {
383 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700384 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800385 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800386
Jack Hea355e5e2017-08-22 16:06:54 -0700387 runOrQueueCallback(new Runnable() {
388 @Override
389 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700390 final BluetoothGattCallback callback = mCallback;
391 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700392 if (status == 0) characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700393 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700394 status);
395 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700396 }
Jack Hea355e5e2017-08-22 16:06:54 -0700397 });
398 }
399
400 /**
401 * Characteristic has been written to the remote device.
402 * Let the app know how we did...
403 * @hide
404 */
405 @Override
406 public void onCharacteristicWrite(String address, int status, int handle) {
407 if (VDBG) {
408 Log.d(TAG, "onCharacteristicWrite() - Device=" + address
409 + " handle=" + handle + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700410 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800411
Jack Hea355e5e2017-08-22 16:06:54 -0700412 if (!address.equals(mDevice.getAddress())) {
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700413 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800414 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800415
Jack Hea355e5e2017-08-22 16:06:54 -0700416 synchronized (mDeviceBusy) {
417 mDeviceBusy = false;
418 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800419
Jack Hea355e5e2017-08-22 16:06:54 -0700420 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
421 handle);
422 if (characteristic == null) return;
423
424 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
425 || status == GATT_INSUFFICIENT_ENCRYPTION)
426 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
427 try {
Jack He2992cd02017-08-22 21:21:23 -0700428 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
429 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700430 mService.writeCharacteristic(mClientIf, address, handle,
431 characteristic.getWriteType(), authReq,
432 characteristic.getValue());
433 mAuthRetryState++;
434 return;
435 } catch (RemoteException e) {
436 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700437 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700438 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800439
Jack Hea355e5e2017-08-22 16:06:54 -0700440 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
441
442 runOrQueueCallback(new Runnable() {
443 @Override
444 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700445 final BluetoothGattCallback callback = mCallback;
446 if (callback != null) {
447 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700448 status);
449 }
450 }
451 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800452 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700453
Jack Hea355e5e2017-08-22 16:06:54 -0700454 /**
455 * Remote characteristic has been updated.
456 * Updates the internal value.
457 * @hide
458 */
459 @Override
460 public void onNotify(String address, int handle, byte[] value) {
461 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
462
463 if (!address.equals(mDevice.getAddress())) {
464 return;
465 }
466
467 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
468 handle);
469 if (characteristic == null) return;
470
Jack Hea355e5e2017-08-22 16:06:54 -0700471 runOrQueueCallback(new Runnable() {
472 @Override
473 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700474 final BluetoothGattCallback callback = mCallback;
475 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700476 characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700477 callback.onCharacteristicChanged(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700478 characteristic);
479 }
480 }
481 });
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700482 }
483
Jack Hea355e5e2017-08-22 16:06:54 -0700484 /**
485 * Descriptor has been read.
486 * @hide
487 */
488 @Override
489 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
490 if (VDBG) {
491 Log.d(TAG,
492 "onDescriptorRead() - Device=" + address + " handle=" + handle);
493 }
494
495 if (!address.equals(mDevice.getAddress())) {
496 return;
497 }
498
499 synchronized (mDeviceBusy) {
500 mDeviceBusy = false;
501 }
502
503 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
504 if (descriptor == null) return;
505
Jack Hea355e5e2017-08-22 16:06:54 -0700506
507 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
508 || status == GATT_INSUFFICIENT_ENCRYPTION)
509 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
510 try {
Jack He2992cd02017-08-22 21:21:23 -0700511 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
512 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700513 mService.readDescriptor(mClientIf, address, handle, authReq);
514 mAuthRetryState++;
515 return;
516 } catch (RemoteException e) {
517 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700518 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700519 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800520
Jack Hea355e5e2017-08-22 16:06:54 -0700521 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
522
523 runOrQueueCallback(new Runnable() {
524 @Override
525 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700526 final BluetoothGattCallback callback = mCallback;
527 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700528 if (status == 0) descriptor.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700529 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700530 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700531 }
Jack Hea355e5e2017-08-22 16:06:54 -0700532 });
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700533 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700534
Jack Hea355e5e2017-08-22 16:06:54 -0700535 /**
536 * Descriptor write operation complete.
537 * @hide
538 */
539 @Override
540 public void onDescriptorWrite(String address, int status, int handle) {
541 if (VDBG) {
542 Log.d(TAG,
543 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
544 }
545
546 if (!address.equals(mDevice.getAddress())) {
547 return;
548 }
549
550 synchronized (mDeviceBusy) {
551 mDeviceBusy = false;
552 }
553
554 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
555 if (descriptor == null) return;
556
557 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
558 || status == GATT_INSUFFICIENT_ENCRYPTION)
559 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
560 try {
Jack He2992cd02017-08-22 21:21:23 -0700561 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
562 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700563 mService.writeDescriptor(mClientIf, address, handle,
564 authReq, descriptor.getValue());
565 mAuthRetryState++;
566 return;
567 } catch (RemoteException e) {
568 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700569 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700570 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700571
Jack Hea355e5e2017-08-22 16:06:54 -0700572 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
573
574 runOrQueueCallback(new Runnable() {
575 @Override
576 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700577 final BluetoothGattCallback callback = mCallback;
578 if (callback != null) {
579 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700580 }
581 }
582 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700583 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700584
Jack Hea355e5e2017-08-22 16:06:54 -0700585 /**
586 * Prepared write transaction completed (or aborted)
587 * @hide
588 */
589 @Override
590 public void onExecuteWrite(String address, int status) {
591 if (VDBG) {
592 Log.d(TAG, "onExecuteWrite() - Device=" + address
593 + " status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700594 }
Jack Hea355e5e2017-08-22 16:06:54 -0700595 if (!address.equals(mDevice.getAddress())) {
596 return;
597 }
598
599 synchronized (mDeviceBusy) {
600 mDeviceBusy = false;
601 }
602
603 runOrQueueCallback(new Runnable() {
604 @Override
605 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700606 final BluetoothGattCallback callback = mCallback;
607 if (callback != null) {
608 callback.onReliableWriteCompleted(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700609 }
610 }
611 });
612 }
613
614 /**
615 * Remote device RSSI has been read
616 * @hide
617 */
618 @Override
619 public void onReadRemoteRssi(String address, int rssi, int status) {
620 if (VDBG) {
Jack He2992cd02017-08-22 21:21:23 -0700621 Log.d(TAG, "onReadRemoteRssi() - Device=" + address
622 + " rssi=" + rssi + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700623 }
624 if (!address.equals(mDevice.getAddress())) {
625 return;
626 }
627 runOrQueueCallback(new Runnable() {
628 @Override
629 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700630 final BluetoothGattCallback callback = mCallback;
631 if (callback != null) {
632 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700633 }
634 }
635 });
636 }
637
638 /**
639 * Callback invoked when the MTU for a given connection changes
640 * @hide
641 */
642 @Override
643 public void onConfigureMTU(String address, int mtu, int status) {
644 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700645 Log.d(TAG, "onConfigureMTU() - Device=" + address
646 + " mtu=" + mtu + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700647 }
648 if (!address.equals(mDevice.getAddress())) {
649 return;
650 }
651
652 runOrQueueCallback(new Runnable() {
653 @Override
654 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700655 final BluetoothGattCallback callback = mCallback;
656 if (callback != null) {
657 callback.onMtuChanged(BluetoothGatt.this, mtu, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700658 }
659 }
660 });
661 }
662
663 /**
664 * Callback invoked when the given connection is updated
665 * @hide
666 */
667 @Override
668 public void onConnectionUpdated(String address, int interval, int latency,
669 int timeout, int status) {
670 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700671 Log.d(TAG, "onConnectionUpdated() - Device=" + address
672 + " interval=" + interval + " latency=" + latency
673 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700674 }
675 if (!address.equals(mDevice.getAddress())) {
676 return;
677 }
678
679 runOrQueueCallback(new Runnable() {
680 @Override
681 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700682 final BluetoothGattCallback callback = mCallback;
683 if (callback != null) {
684 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
Jack Hea355e5e2017-08-22 16:06:54 -0700685 timeout, status);
686 }
687 }
688 });
689 }
690 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800691
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700692 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700693 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800694 mService = iGatt;
695 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700696 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800697 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700698 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800699 mServices = new ArrayList<BluetoothGattService>();
700
Matthew Xieddf7e472013-03-01 18:41:02 -0800701 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700702 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800703 }
704
705 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700706 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700707 *
708 * Application should call this method as early as possible after it is done with
709 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800710 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700711 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800712 if (DBG) Log.d(TAG, "close()");
713
714 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700715 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700716 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800717 }
718
719 /**
720 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700721 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800722 * @hide
723 */
724 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700725 int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700726 for (BluetoothGattService svc : mServices) {
Jack He2992cd02017-08-22 21:21:23 -0700727 if (svc.getDevice().equals(device)
Jack He2992cd02017-08-22 21:21:23 -0700728 && svc.getInstanceId() == instanceId
729 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800730 return svc;
731 }
732 }
733 return null;
734 }
735
736
737 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700738 * Returns a characteristic with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700739 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700740 * @hide
741 */
Jack Hea355e5e2017-08-22 16:06:54 -0700742 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
743 int instanceId) {
744 for (BluetoothGattService svc : mServices) {
745 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
746 if (charac.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700747 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700748 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700749 }
750 }
751 return null;
752 }
753
754 /**
755 * Returns a descriptor with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700756 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700757 * @hide
758 */
759 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700760 for (BluetoothGattService svc : mServices) {
761 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
762 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
763 if (desc.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700764 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700765 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700766 }
767 }
768 }
769 return null;
770 }
771
772 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700773 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
774 * immediately if no Handler was provided.
775 */
776 private void runOrQueueCallback(final Runnable cb) {
777 if (mHandler == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700778 try {
779 cb.run();
780 } catch (Exception ex) {
781 Log.w(TAG, "Unhandled exception in callback", ex);
782 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700783 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700784 mHandler.post(cb);
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700785 }
786 }
787
788 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800789 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700791 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800792 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800793 *
794 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
795 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800796 * @param callback GATT callback handler that will receive asynchronous callbacks.
Jack Hea355e5e2017-08-22 16:06:54 -0700797 * @return If true, the callback will be called to notify success or failure, false on immediate
798 * error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800799 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700800 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800801 if (DBG) Log.d(TAG, "registerApp()");
802 if (mService == null) return false;
803
804 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700805 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 UUID uuid = UUID.randomUUID();
807 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
808
809 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700810 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800811 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700812 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800813 return false;
814 }
815
816 return true;
817 }
818
819 /**
820 * Unregister the current application and callbacks.
821 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +0100822 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -0800823 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800824 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
825 if (mService == null || mClientIf == 0) return;
826
827 try {
828 mCallback = null;
829 mService.unregisterClient(mClientIf);
830 mClientIf = 0;
831 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700832 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800833 }
834 }
835
836 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800837 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800838 *
839 * <p>The connection may not be established right away, but will be
840 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700841 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800842 * invoked when the connection state changes as a result of this function.
843 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700844 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800845 * the remote device, or rather passively scan and finalize the connection
846 * when the remote device is in range/available. Generally, the first ever
847 * connection to a device should be direct (autoConnect set to false) and
848 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800849 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800850 *
851 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
852 *
853 * @param device Remote device to connect to
Jack Hea355e5e2017-08-22 16:06:54 -0700854 * @param autoConnect Whether to directly connect to the remote device (false) or to
855 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800856 * @return true, if the connection attempt was initiated successfully
857 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +0100858 @UnsupportedAppUsage
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700859 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700860 Handler handler) {
861 if (DBG) {
862 Log.d(TAG,
863 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
864 }
865 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800866 if (mConnState != CONN_STATE_IDLE) {
867 throw new IllegalStateException("Not idle");
868 }
869 mConnState = CONN_STATE_CONNECTING;
870 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700871
872 mAutoConnect = autoConnect;
873
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700874 if (!registerApp(callback, handler)) {
Jack Hea355e5e2017-08-22 16:06:54 -0700875 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800876 mConnState = CONN_STATE_IDLE;
877 }
878 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800879 return false;
880 }
881
Sungki Kimd35167a2016-05-19 10:18:07 -0700882 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800883 return true;
884 }
885
886 /**
887 * Disconnects an established connection, or cancels a connection attempt
888 * currently in progress.
889 *
890 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800891 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800892 public void disconnect() {
893 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800894 if (mService == null || mClientIf == 0) return;
895
896 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800897 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800898 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700899 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800900 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700901 }
902
903 /**
904 * Connect back to remote device.
905 *
906 * <p>This method is used to re-connect to a remote device after the
907 * connection has been dropped. If the device is not in range, the
908 * re-connection will be triggered once the device is back in range.
909 *
910 * @return true, if the connection attempt was initiated successfully
911 */
912 public boolean connect() {
913 try {
Jack He13f52c82017-07-05 14:55:35 -0700914 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
915 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700916 return true;
917 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700918 Log.e(TAG, "", e);
Matthew Xie33ec9842013-04-03 00:29:27 -0700919 return false;
920 }
921 }
922
923 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800924 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskicf733cd2017-09-14 08:51:44 -0700925 * recommendation, whether the PHY change will happen depends on other applications preferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800926 * local and remote controller capabilities. Controller can override these settings.
927 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700928 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800929 * if no PHY change happens. It is also triggered when remote device updates the PHY.
930 *
Jack Hea355e5e2017-08-22 16:06:54 -0700931 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
932 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
933 * BluetoothDevice#PHY_LE_CODED_MASK}.
934 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
935 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
936 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800937 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700938 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
939 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800940 */
941 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
942 try {
943 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700944 phyOptions);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800945 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700946 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800947 }
948 }
949
950 /**
951 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700952 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800953 */
954 public void readPhy() {
955 try {
956 mService.clientReadPhy(mClientIf, mDevice.getAddress());
957 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700958 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800959 }
960 }
961
962 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700963 * Return the remote bluetooth device this GATT client targets to
964 *
965 * @return remote bluetooth device
966 */
967 public BluetoothDevice getDevice() {
968 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800969 }
970
971 /**
972 * Discovers services offered by a remote device as well as their
973 * characteristics and descriptors.
974 *
975 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700976 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800977 * triggered. If the discovery was successful, the remote services can be
978 * retrieved using the {@link #getServices} function.
979 *
980 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
981 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800982 * @return true, if the remote service discovery has been started
983 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800984 public boolean discoverServices() {
985 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800986 if (mService == null || mClientIf == 0) return false;
987
988 mServices.clear();
989
990 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800991 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800992 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700993 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800994 return false;
995 }
996
997 return true;
998 }
999
1000 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -07001001 * Discovers a service by UUID. This is exposed only for passing PTS tests.
1002 * It should never be used by real applications. The service is not searched
1003 * for characteristics and descriptors, or returned in any callback.
1004 *
1005 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1006 *
1007 * @return true, if the remote service discovery has been started
1008 * @hide
1009 */
1010 public boolean discoverServiceByUuid(UUID uuid) {
1011 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
1012 if (mService == null || mClientIf == 0) return false;
1013
1014 mServices.clear();
1015
1016 try {
1017 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
1018 } catch (RemoteException e) {
1019 Log.e(TAG, "", e);
1020 return false;
1021 }
1022 return true;
1023 }
1024
1025 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001026 * Returns a list of GATT services offered by the remote device.
1027 *
1028 * <p>This function requires that service discovery has been completed
1029 * for the given device.
1030 *
1031 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1032 *
Jack Hea355e5e2017-08-22 16:06:54 -07001033 * @return List of services on the remote device. Returns an empty list if service discovery has
1034 * not yet been performed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001035 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001036 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001037 List<BluetoothGattService> result =
1038 new ArrayList<BluetoothGattService>();
1039
1040 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001041 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001042 result.add(service);
1043 }
1044 }
1045
1046 return result;
1047 }
1048
1049 /**
1050 * Returns a {@link BluetoothGattService}, if the requested UUID is
1051 * supported by the remote device.
1052 *
1053 * <p>This function requires that service discovery has been completed
1054 * for the given device.
1055 *
1056 * <p>If multiple instances of the same service (as identified by UUID)
1057 * exist, the first instance of the service is returned.
1058 *
1059 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1060 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001061 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -07001062 * @return BluetoothGattService if supported, or null if the requested service is not offered by
1063 * the remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001064 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001065 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001066 for (BluetoothGattService service : mServices) {
Jack He2992cd02017-08-22 21:21:23 -07001067 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001068 return service;
1069 }
1070 }
1071
1072 return null;
1073 }
1074
1075 /**
1076 * Reads the requested characteristic from the associated remote device.
1077 *
1078 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001079 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001080 * callback.
1081 *
1082 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1083 *
1084 * @param characteristic Characteristic to read from the remote device
1085 * @return true, if the read operation was initiated successfully
1086 */
1087 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
Jack He2992cd02017-08-22 21:21:23 -07001088 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001089 return false;
1090 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001091
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001092 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001093 if (mService == null || mClientIf == 0) return false;
1094
1095 BluetoothGattService service = characteristic.getService();
1096 if (service == null) return false;
1097
1098 BluetoothDevice device = service.getDevice();
1099 if (device == null) return false;
1100
Jack Hea355e5e2017-08-22 16:06:54 -07001101 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001102 if (mDeviceBusy) return false;
1103 mDeviceBusy = true;
1104 }
1105
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001106 try {
1107 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001108 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001109 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001110 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001111 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001112 return false;
1113 }
1114
1115 return true;
1116 }
1117
1118 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001119 * Reads the characteristic using its UUID from the associated remote device.
1120 *
1121 * <p>This is an asynchronous operation. The result of the read operation
1122 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1123 * callback.
1124 *
1125 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1126 *
1127 * @param uuid UUID of characteristic to read from the remote device
1128 * @return true, if the read operation was initiated successfully
1129 * @hide
1130 */
1131 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1132 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1133 if (mService == null || mClientIf == 0) return false;
1134
Jack Hea355e5e2017-08-22 16:06:54 -07001135 synchronized (mDeviceBusy) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001136 if (mDeviceBusy) return false;
1137 mDeviceBusy = true;
1138 }
1139
1140 try {
1141 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001142 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001143 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001144 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001145 mDeviceBusy = false;
1146 return false;
1147 }
1148
1149 return true;
1150 }
1151
1152
1153 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001154 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001155 *
1156 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001157 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001158 * reporting the result of the operation.
1159 *
1160 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1161 *
1162 * @param characteristic Characteristic to write on the remote device
1163 * @return true, if the write operation was initiated successfully
1164 */
1165 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1166 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack He2992cd02017-08-22 21:21:23 -07001167 && (characteristic.getProperties()
1168 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001169 return false;
1170 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001171
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001172 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001173 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001174
1175 BluetoothGattService service = characteristic.getService();
1176 if (service == null) return false;
1177
1178 BluetoothDevice device = service.getDevice();
1179 if (device == null) return false;
1180
Jack Hea355e5e2017-08-22 16:06:54 -07001181 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001182 if (mDeviceBusy) return false;
1183 mDeviceBusy = true;
1184 }
1185
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001186 try {
1187 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001188 characteristic.getInstanceId(), characteristic.getWriteType(),
1189 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001190 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001191 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001192 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001193 return false;
1194 }
1195
1196 return true;
1197 }
1198
1199 /**
1200 * Reads the value for a given descriptor from the associated remote device.
1201 *
1202 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001203 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001204 * triggered, signaling the result of the operation.
1205 *
1206 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1207 *
1208 * @param descriptor Descriptor value to read from the remote device
1209 * @return true, if the read operation was initiated successfully
1210 */
1211 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001212 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001213 if (mService == null || mClientIf == 0) return false;
1214
1215 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1216 if (characteristic == null) return false;
1217
1218 BluetoothGattService service = characteristic.getService();
1219 if (service == null) return false;
1220
1221 BluetoothDevice device = service.getDevice();
1222 if (device == null) return false;
1223
Jack Hea355e5e2017-08-22 16:06:54 -07001224 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001225 if (mDeviceBusy) return false;
1226 mDeviceBusy = true;
1227 }
1228
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001229 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001230 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001231 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001232 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001233 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001234 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001235 return false;
1236 }
1237
1238 return true;
1239 }
1240
1241 /**
1242 * Write the value of a given descriptor to the associated remote device.
1243 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001244 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001245 * triggered to report the result of the write operation.
1246 *
1247 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1248 *
1249 * @param descriptor Descriptor to write to the associated remote device
1250 * @return true, if the write operation was initiated successfully
1251 */
1252 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001253 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001254 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001255
1256 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1257 if (characteristic == null) return false;
1258
1259 BluetoothGattService service = characteristic.getService();
1260 if (service == null) return false;
1261
1262 BluetoothDevice device = service.getDevice();
1263 if (device == null) return false;
1264
Jack Hea355e5e2017-08-22 16:06:54 -07001265 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001266 if (mDeviceBusy) return false;
1267 mDeviceBusy = true;
1268 }
1269
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001270 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001271 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001272 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001273 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001274 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001275 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001276 return false;
1277 }
1278
1279 return true;
1280 }
1281
1282 /**
1283 * Initiates a reliable write transaction for a given remote device.
1284 *
1285 * <p>Once a reliable write transaction has been initiated, all calls
1286 * to {@link #writeCharacteristic} are sent to the remote device for
1287 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001288 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001289 * in response to every {@link #writeCharacteristic} call and is responsible
1290 * for verifying if the value has been transmitted accurately.
1291 *
1292 * <p>After all characteristics have been queued up and verified,
1293 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1294 * was not written correctly, calling {@link #abortReliableWrite} will
1295 * cancel the current transaction without commiting any values on the
1296 * remote device.
1297 *
1298 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1299 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001300 * @return true, if the reliable write transaction has been initiated
1301 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001302 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001303 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001304 if (mService == null || mClientIf == 0) return false;
1305
1306 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001307 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001308 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001309 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001310 return false;
1311 }
1312
1313 return true;
1314 }
1315
1316 /**
1317 * Executes a reliable write transaction for a given remote device.
1318 *
1319 * <p>This function will commit all queued up characteristic write
1320 * operations for a given remote device.
1321 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001322 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001323 * invoked to indicate whether the transaction has been executed correctly.
1324 *
1325 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1326 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001327 * @return true, if the request to execute the transaction has been sent
1328 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001329 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001330 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001331 if (mService == null || mClientIf == 0) return false;
1332
Jack Hea355e5e2017-08-22 16:06:54 -07001333 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001334 if (mDeviceBusy) return false;
1335 mDeviceBusy = true;
1336 }
1337
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001338 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001339 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001340 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001341 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001342 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001343 return false;
1344 }
1345
1346 return true;
1347 }
1348
1349 /**
1350 * Cancels a reliable write transaction for a given device.
1351 *
1352 * <p>Calling this function will discard all queued characteristic write
1353 * operations for a given remote device.
1354 *
1355 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001356 */
John Du48f8b5d2013-08-19 12:20:37 -07001357 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001358 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001359 if (mService == null || mClientIf == 0) return;
1360
1361 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001362 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001363 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001364 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001365 }
1366 }
1367
1368 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001369 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001370 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001371 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001372 public void abortReliableWrite(BluetoothDevice mDevice) {
1373 abortReliableWrite();
1374 }
1375
1376 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001377 * Enable or disable notifications/indications for a given characteristic.
1378 *
1379 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001380 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001381 * triggered if the remote device indicates that the given characteristic
1382 * has changed.
1383 *
1384 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1385 *
1386 * @param characteristic The characteristic for which to enable notifications
1387 * @param enable Set to true to enable notifications/indications
1388 * @return true, if the requested notification status was set successfully
1389 */
1390 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001391 boolean enable) {
1392 if (DBG) {
1393 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1394 + " enable: " + enable);
1395 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001396 if (mService == null || mClientIf == 0) return false;
1397
1398 BluetoothGattService service = characteristic.getService();
1399 if (service == null) return false;
1400
1401 BluetoothDevice device = service.getDevice();
1402 if (device == null) return false;
1403
1404 try {
1405 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001406 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001407 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001408 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001409 return false;
1410 }
1411
1412 return true;
1413 }
1414
1415 /**
1416 * Clears the internal cache and forces a refresh of the services from the
1417 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001418 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001419 * @hide
1420 */
Mathew Inwood7acad5e2018-08-01 15:00:35 +01001421 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -08001422 public boolean refresh() {
1423 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001424 if (mService == null || mClientIf == 0) return false;
1425
1426 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001427 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001428 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001429 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001430 return false;
1431 }
1432
1433 return true;
1434 }
1435
1436 /**
1437 * Read the RSSI for a connected remote device.
1438 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001439 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001440 * invoked when the RSSI value has been read.
1441 *
1442 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1443 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001444 * @return true, if the RSSI value has been requested successfully
1445 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001446 public boolean readRemoteRssi() {
1447 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001448 if (mService == null || mClientIf == 0) return false;
1449
1450 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001451 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001452 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001453 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001454 return false;
1455 }
1456
1457 return true;
1458 }
1459
1460 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001461 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001462 *
1463 * <p>When performing a write request operation (write without response),
1464 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001465 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001466 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001467 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001468 * whether this operation was successful.
1469 *
1470 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1471 *
1472 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001473 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001474 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001475 if (DBG) {
1476 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1477 + " mtu: " + mtu);
1478 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001479 if (mService == null || mClientIf == 0) return false;
1480
1481 try {
1482 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1483 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001484 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001485 return false;
1486 }
1487
1488 return true;
1489 }
1490
1491 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001492 * Request a connection parameter update.
1493 *
1494 * <p>This function will send a connection parameter update request to the
1495 * remote device.
1496 *
Jack Hea355e5e2017-08-22 16:06:54 -07001497 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1498 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1499 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1500 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001501 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001502 public boolean requestConnectionPriority(int connectionPriority) {
Jack He2992cd02017-08-22 21:21:23 -07001503 if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1504 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001505 throw new IllegalArgumentException("connectionPriority not within valid range");
1506 }
1507
Andre Eisenbach4072da02014-08-19 17:58:55 -07001508 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001509 if (mService == null || mClientIf == 0) return false;
1510
1511 try {
1512 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1513 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001514 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001515 return false;
1516 }
1517
1518 return true;
1519 }
1520
1521 /**
Stanley Tng6da1dda2018-01-04 15:42:25 -08001522 * Request an LE connection parameter update.
1523 *
1524 * <p>This function will send an LE connection parameters update request to the remote device.
1525 *
1526 * @return true, if the request is send to the Bluetooth stack.
1527 * @hide
1528 */
Stanley Tng148dd5b2018-03-19 12:28:56 -07001529 public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
1530 int slaveLatency, int supervisionTimeout,
1531 int minConnectionEventLen, int maxConnectionEventLen) {
Stanley Tng6da1dda2018-01-04 15:42:25 -08001532 if (DBG) {
1533 Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
Stanley Tng148dd5b2018-03-19 12:28:56 -07001534 + ")" + (1.25 * minConnectionInterval)
1535 + "msec, max=(" + maxConnectionInterval + ")"
Stanley Tng6da1dda2018-01-04 15:42:25 -08001536 + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
Stanley Tng148dd5b2018-03-19 12:28:56 -07001537 + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
1538 + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001539 }
1540 if (mService == null || mClientIf == 0) return false;
1541
1542 try {
1543 mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
Stanley Tng148dd5b2018-03-19 12:28:56 -07001544 minConnectionInterval, maxConnectionInterval,
1545 slaveLatency, supervisionTimeout,
1546 minConnectionEventLen, maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001547 } catch (RemoteException e) {
1548 Log.e(TAG, "", e);
1549 return false;
1550 }
1551
1552 return true;
1553 }
1554
1555 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001556 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1557 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001558 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001559 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001560 */
1561 @Override
1562 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001563 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001564 }
1565
1566 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001567 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1568 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001569 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001570 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001571 */
1572 @Override
1573 public List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -07001574 throw new UnsupportedOperationException(
1575 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001576 }
1577
1578 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001579 * Not supported - please use
1580 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1581 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001582 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001583 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001584 */
1585 @Override
1586 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -07001587 throw new UnsupportedOperationException(
1588 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001589 }
1590}