blob: 78248efdd0480a66489280e19a6f69a0d6596541 [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 Inwood4dc66d32018-08-01 15:07:20 +010019import android.annotation.UnsupportedAppUsage;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070020import android.os.Handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080021import android.os.ParcelUuid;
22import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080023import android.util.Log;
24
25import java.util.ArrayList;
26import java.util.List;
27import java.util.UUID;
28
29/**
Matthew Xieddf7e472013-03-01 18:41:02 -080030 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080031 *
Matthew Xieddf7e472013-03-01 18:41:02 -080032 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080033 * with Bluetooth Smart or Smart Ready devices.
34 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070035 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070036 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080037 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
38 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080039 */
40public final class BluetoothGatt implements BluetoothProfile {
41 private static final String TAG = "BluetoothGatt";
42 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070043 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044
Mathew Inwood4dc66d32018-08-01 15:07:20 +010045 @UnsupportedAppUsage
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080046 private IBluetoothGatt mService;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010047 @UnsupportedAppUsage
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -070048 private volatile BluetoothGattCallback mCallback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070049 private Handler mHandler;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010050 @UnsupportedAppUsage
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080051 private int mClientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -080052 private BluetoothDevice mDevice;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010053 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -080054 private boolean mAutoConnect;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010055 @UnsupportedAppUsage
Jacky Cheung3854e222016-10-20 13:55:21 -070056 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080057 private int mConnState;
58 private final Object mStateLock = new Object();
Mathew Inwood4dc66d32018-08-01 15:07:20 +010059 @UnsupportedAppUsage
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070060 private Boolean mDeviceBusy = false;
Mathew Inwood4dc66d32018-08-01 15:07:20 +010061 @UnsupportedAppUsage
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070062 private int mTransport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080063 private int mPhy;
Jack He13f52c82017-07-05 14:55:35 -070064 private boolean mOpportunistic;
Matthew Xieddf7e472013-03-01 18:41:02 -080065
Jacky Cheung3854e222016-10-20 13:55:21 -070066 private static final int AUTH_RETRY_STATE_IDLE = 0;
67 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
68 private static final int AUTH_RETRY_STATE_MITM = 2;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 private static final int CONN_STATE_IDLE = 0;
71 private static final int CONN_STATE_CONNECTING = 1;
72 private static final int CONN_STATE_CONNECTED = 2;
73 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070074 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080075
76 private List<BluetoothGattService> mServices;
77
Matthew Xieddf7e472013-03-01 18:41:02 -080078 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080079 public static final int GATT_SUCCESS = 0;
80
Matthew Xieddf7e472013-03-01 18:41:02 -080081 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080082 public static final int GATT_READ_NOT_PERMITTED = 0x2;
83
Matthew Xieddf7e472013-03-01 18:41:02 -080084 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080085 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
86
87 /** Insufficient authentication for a given operation */
88 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
89
90 /** The given request is not supported */
91 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
92
93 /** Insufficient encryption for a given operation */
94 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
95
96 /** A read or write operation was requested with an invalid offset */
97 public static final int GATT_INVALID_OFFSET = 0x7;
98
99 /** A write operation exceeds the maximum length of the attribute */
100 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
101
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -0700102 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700103 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
104
Matthew Xie90ca8072013-05-28 21:06:50 +0000105 /** A GATT operation failed, errors other than the above */
106 public static final int GATT_FAILURE = 0x101;
107
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800108 /**
Stanley Tng505c0582018-05-03 08:50:49 -0700109 * Connection parameter update - Use the connection parameters recommended by the
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700110 * Bluetooth SIG. This is the default value if no connection parameter update
111 * is requested.
112 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700113 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700114
115 /**
Stanley Tng505c0582018-05-03 08:50:49 -0700116 * Connection parameter update - Request a high priority, low latency connection.
117 * An application should only request high priority connection parameters to transfer large
118 * amounts of data over LE quickly. Once the transfer is complete, the application should
119 * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce
120 * energy use.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700121 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700122 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700123
Stanley Tng505c0582018-05-03 08:50:49 -0700124 /** Connection parameter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700125 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700126
127 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800128 * No authentication required.
Jack Hea355e5e2017-08-22 16:06:54 -0700129 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800130 * @hide
131 */
132 /*package*/ static final int AUTHENTICATION_NONE = 0;
133
134 /**
135 * Authentication requested; no man-in-the-middle protection required.
Jack Hea355e5e2017-08-22 16:06:54 -0700136 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 * @hide
138 */
139 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
140
141 /**
142 * Authentication with man-in-the-middle protection requested.
Jack Hea355e5e2017-08-22 16:06:54 -0700143 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800144 * @hide
145 */
146 /*package*/ static final int AUTHENTICATION_MITM = 2;
147
148 /**
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700149 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800150 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -0700151 private final IBluetoothGattCallback mBluetoothGattCallback =
Jack Hea355e5e2017-08-22 16:06:54 -0700152 new IBluetoothGattCallback.Stub() {
153 /**
154 * Application interface registered - app is ready to go
155 * @hide
156 */
157 @Override
158 public void onClientRegistered(int status, int clientIf) {
159 if (DBG) {
160 Log.d(TAG, "onClientRegistered() - status=" + status
161 + " clientIf=" + clientIf);
162 }
163 if (VDBG) {
164 synchronized (mStateLock) {
165 if (mConnState != CONN_STATE_CONNECTING) {
166 Log.e(TAG, "Bad connection state: " + mConnState);
167 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800168 }
169 }
Jack Hea355e5e2017-08-22 16:06:54 -0700170 mClientIf = clientIf;
171 if (status != GATT_SUCCESS) {
172 runOrQueueCallback(new Runnable() {
173 @Override
174 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700175 final BluetoothGattCallback callback = mCallback;
176 if (callback != null) {
177 callback.onConnectionStateChange(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700178 GATT_FAILURE,
179 BluetoothProfile.STATE_DISCONNECTED);
180 }
181 }
182 });
183
184 synchronized (mStateLock) {
185 mConnState = CONN_STATE_IDLE;
186 }
187 return;
188 }
189 try {
190 mService.clientConnect(mClientIf, mDevice.getAddress(),
191 !mAutoConnect, mTransport, mOpportunistic,
192 mPhy); // autoConnect is inverse of "isDirect"
193 } catch (RemoteException e) {
194 Log.e(TAG, "", e);
195 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800196 }
Jack Hea355e5e2017-08-22 16:06:54 -0700197
198 /**
199 * Phy update callback
200 * @hide
201 */
202 @Override
203 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
204 if (DBG) {
205 Log.d(TAG, "onPhyUpdate() - status=" + status
206 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
207 }
208 if (!address.equals(mDevice.getAddress())) {
209 return;
210 }
211
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700212 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700213 @Override
214 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700215 final BluetoothGattCallback callback = mCallback;
216 if (callback != null) {
217 callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700218 }
219 }
220 });
221 }
222
223 /**
224 * Phy read callback
225 * @hide
226 */
227 @Override
228 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
229 if (DBG) {
230 Log.d(TAG, "onPhyRead() - status=" + status
231 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
232 }
233 if (!address.equals(mDevice.getAddress())) {
234 return;
235 }
236
237 runOrQueueCallback(new Runnable() {
238 @Override
239 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700240 final BluetoothGattCallback callback = mCallback;
241 if (callback != null) {
242 callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700243 }
244 }
245 });
246 }
247
248 /**
249 * Client connection state changed
250 * @hide
251 */
252 @Override
253 public void onClientConnectionState(int status, int clientIf,
254 boolean connected, String address) {
255 if (DBG) {
256 Log.d(TAG, "onClientConnectionState() - status=" + status
257 + " clientIf=" + clientIf + " device=" + address);
258 }
259 if (!address.equals(mDevice.getAddress())) {
260 return;
261 }
262 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
263 BluetoothProfile.STATE_DISCONNECTED;
264
265 runOrQueueCallback(new Runnable() {
266 @Override
267 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700268 final BluetoothGattCallback callback = mCallback;
269 if (callback != null) {
270 callback.onConnectionStateChange(BluetoothGatt.this, status,
Jack Hea355e5e2017-08-22 16:06:54 -0700271 profileState);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700272 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700273 }
274 });
275
Jack Hea355e5e2017-08-22 16:06:54 -0700276 synchronized (mStateLock) {
277 if (connected) {
278 mConnState = CONN_STATE_CONNECTED;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800279 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700280 mConnState = CONN_STATE_IDLE;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800281 }
282 }
Jack Hea355e5e2017-08-22 16:06:54 -0700283
284 synchronized (mDeviceBusy) {
285 mDeviceBusy = false;
286 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800287 }
288
Jack Hea355e5e2017-08-22 16:06:54 -0700289 /**
290 * Remote search has been completed.
291 * The internal object structure should now reflect the state
292 * of the remote device database. Let the application know that
293 * we are done at this point.
294 * @hide
295 */
296 @Override
297 public void onSearchComplete(String address, List<BluetoothGattService> services,
298 int status) {
299 if (DBG) {
300 Log.d(TAG,
301 "onSearchComplete() = Device=" + address + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700302 }
Jack Hea355e5e2017-08-22 16:06:54 -0700303 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800304 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800305 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306
Jack Hea355e5e2017-08-22 16:06:54 -0700307 for (BluetoothGattService s : services) {
308 //services we receive don't have device set properly.
309 s.setDevice(mDevice);
310 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800311
Jack Hea355e5e2017-08-22 16:06:54 -0700312 mServices.addAll(services);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800313
Jack Hea355e5e2017-08-22 16:06:54 -0700314 // Fix references to included services, as they doesn't point to right objects.
315 for (BluetoothGattService fixedService : mServices) {
316 ArrayList<BluetoothGattService> includedServices =
317 new ArrayList(fixedService.getIncludedServices());
318 fixedService.getIncludedServices().clear();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800319
Jack Hea355e5e2017-08-22 16:06:54 -0700320 for (BluetoothGattService brokenRef : includedServices) {
321 BluetoothGattService includedService = getService(mDevice,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700322 brokenRef.getUuid(), brokenRef.getInstanceId());
Jack Hea355e5e2017-08-22 16:06:54 -0700323 if (includedService != null) {
324 fixedService.addIncludedService(includedService);
325 } else {
326 Log.e(TAG, "Broken GATT database: can't find included service.");
327 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700328 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700329 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800330
Jack Hea355e5e2017-08-22 16:06:54 -0700331 runOrQueueCallback(new Runnable() {
332 @Override
333 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700334 final BluetoothGattCallback callback = mCallback;
335 if (callback != null) {
336 callback.onServicesDiscovered(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700337 }
338 }
339 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800340 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700341
Jack Hea355e5e2017-08-22 16:06:54 -0700342 /**
343 * Remote characteristic has been read.
344 * Updates the internal value.
345 * @hide
346 */
347 @Override
348 public void onCharacteristicRead(String address, int status, int handle,
349 byte[] value) {
350 if (VDBG) {
351 Log.d(TAG, "onCharacteristicRead() - Device=" + address
352 + " handle=" + handle + " Status=" + status);
353 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700354
Jack Hea355e5e2017-08-22 16:06:54 -0700355 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800356 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800358
Jack Hea355e5e2017-08-22 16:06:54 -0700359 synchronized (mDeviceBusy) {
360 mDeviceBusy = false;
361 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800362
Jack Hea355e5e2017-08-22 16:06:54 -0700363 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
364 || status == GATT_INSUFFICIENT_ENCRYPTION)
365 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
366 try {
Jack He2992cd02017-08-22 21:21:23 -0700367 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
368 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700369 mService.readCharacteristic(mClientIf, address, handle, authReq);
370 mAuthRetryState++;
371 return;
372 } catch (RemoteException e) {
373 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700374 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700375 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800376
Jack Hea355e5e2017-08-22 16:06:54 -0700377 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800378
Jack Hea355e5e2017-08-22 16:06:54 -0700379 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
380 handle);
381 if (characteristic == null) {
382 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700383 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800384 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800385
Jack Hea355e5e2017-08-22 16:06:54 -0700386 runOrQueueCallback(new Runnable() {
387 @Override
388 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700389 final BluetoothGattCallback callback = mCallback;
390 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700391 if (status == 0) characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700392 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700393 status);
394 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700395 }
Jack Hea355e5e2017-08-22 16:06:54 -0700396 });
397 }
398
399 /**
400 * Characteristic has been written to the remote device.
401 * Let the app know how we did...
402 * @hide
403 */
404 @Override
405 public void onCharacteristicWrite(String address, int status, int handle) {
406 if (VDBG) {
407 Log.d(TAG, "onCharacteristicWrite() - Device=" + address
408 + " handle=" + handle + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700409 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800410
Jack Hea355e5e2017-08-22 16:06:54 -0700411 if (!address.equals(mDevice.getAddress())) {
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700412 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800413 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800414
Jack Hea355e5e2017-08-22 16:06:54 -0700415 synchronized (mDeviceBusy) {
416 mDeviceBusy = false;
417 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800418
Jack Hea355e5e2017-08-22 16:06:54 -0700419 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
420 handle);
421 if (characteristic == null) return;
422
423 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
424 || status == GATT_INSUFFICIENT_ENCRYPTION)
425 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
426 try {
Jack He2992cd02017-08-22 21:21:23 -0700427 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
428 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700429 mService.writeCharacteristic(mClientIf, address, handle,
430 characteristic.getWriteType(), authReq,
431 characteristic.getValue());
432 mAuthRetryState++;
433 return;
434 } catch (RemoteException e) {
435 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700436 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700437 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800438
Jack Hea355e5e2017-08-22 16:06:54 -0700439 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
440
441 runOrQueueCallback(new Runnable() {
442 @Override
443 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700444 final BluetoothGattCallback callback = mCallback;
445 if (callback != null) {
446 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700447 status);
448 }
449 }
450 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800451 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700452
Jack Hea355e5e2017-08-22 16:06:54 -0700453 /**
454 * Remote characteristic has been updated.
455 * Updates the internal value.
456 * @hide
457 */
458 @Override
459 public void onNotify(String address, int handle, byte[] value) {
460 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
461
462 if (!address.equals(mDevice.getAddress())) {
463 return;
464 }
465
466 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
467 handle);
468 if (characteristic == null) return;
469
Jack Hea355e5e2017-08-22 16:06:54 -0700470 runOrQueueCallback(new Runnable() {
471 @Override
472 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700473 final BluetoothGattCallback callback = mCallback;
474 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700475 characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700476 callback.onCharacteristicChanged(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700477 characteristic);
478 }
479 }
480 });
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700481 }
482
Jack Hea355e5e2017-08-22 16:06:54 -0700483 /**
484 * Descriptor has been read.
485 * @hide
486 */
487 @Override
488 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
489 if (VDBG) {
490 Log.d(TAG,
491 "onDescriptorRead() - Device=" + address + " handle=" + handle);
492 }
493
494 if (!address.equals(mDevice.getAddress())) {
495 return;
496 }
497
498 synchronized (mDeviceBusy) {
499 mDeviceBusy = false;
500 }
501
502 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
503 if (descriptor == null) return;
504
Jack Hea355e5e2017-08-22 16:06:54 -0700505
506 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
507 || status == GATT_INSUFFICIENT_ENCRYPTION)
508 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
509 try {
Jack He2992cd02017-08-22 21:21:23 -0700510 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
511 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700512 mService.readDescriptor(mClientIf, address, handle, authReq);
513 mAuthRetryState++;
514 return;
515 } catch (RemoteException e) {
516 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700517 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700518 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519
Jack Hea355e5e2017-08-22 16:06:54 -0700520 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
521
522 runOrQueueCallback(new Runnable() {
523 @Override
524 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700525 final BluetoothGattCallback callback = mCallback;
526 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700527 if (status == 0) descriptor.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700528 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700529 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700530 }
Jack Hea355e5e2017-08-22 16:06:54 -0700531 });
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700532 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700533
Jack Hea355e5e2017-08-22 16:06:54 -0700534 /**
535 * Descriptor write operation complete.
536 * @hide
537 */
538 @Override
539 public void onDescriptorWrite(String address, int status, int handle) {
540 if (VDBG) {
541 Log.d(TAG,
542 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
543 }
544
545 if (!address.equals(mDevice.getAddress())) {
546 return;
547 }
548
549 synchronized (mDeviceBusy) {
550 mDeviceBusy = false;
551 }
552
553 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
554 if (descriptor == null) return;
555
556 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
557 || status == GATT_INSUFFICIENT_ENCRYPTION)
558 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
559 try {
Jack He2992cd02017-08-22 21:21:23 -0700560 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
561 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700562 mService.writeDescriptor(mClientIf, address, handle,
563 authReq, descriptor.getValue());
564 mAuthRetryState++;
565 return;
566 } catch (RemoteException e) {
567 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700568 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700569 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700570
Jack Hea355e5e2017-08-22 16:06:54 -0700571 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
572
573 runOrQueueCallback(new Runnable() {
574 @Override
575 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700576 final BluetoothGattCallback callback = mCallback;
577 if (callback != null) {
578 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700579 }
580 }
581 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700582 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700583
Jack Hea355e5e2017-08-22 16:06:54 -0700584 /**
585 * Prepared write transaction completed (or aborted)
586 * @hide
587 */
588 @Override
589 public void onExecuteWrite(String address, int status) {
590 if (VDBG) {
591 Log.d(TAG, "onExecuteWrite() - Device=" + address
592 + " status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700593 }
Jack Hea355e5e2017-08-22 16:06:54 -0700594 if (!address.equals(mDevice.getAddress())) {
595 return;
596 }
597
598 synchronized (mDeviceBusy) {
599 mDeviceBusy = false;
600 }
601
602 runOrQueueCallback(new Runnable() {
603 @Override
604 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700605 final BluetoothGattCallback callback = mCallback;
606 if (callback != null) {
607 callback.onReliableWriteCompleted(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700608 }
609 }
610 });
611 }
612
613 /**
614 * Remote device RSSI has been read
615 * @hide
616 */
617 @Override
618 public void onReadRemoteRssi(String address, int rssi, int status) {
619 if (VDBG) {
Jack He2992cd02017-08-22 21:21:23 -0700620 Log.d(TAG, "onReadRemoteRssi() - Device=" + address
621 + " rssi=" + rssi + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700622 }
623 if (!address.equals(mDevice.getAddress())) {
624 return;
625 }
626 runOrQueueCallback(new Runnable() {
627 @Override
628 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700629 final BluetoothGattCallback callback = mCallback;
630 if (callback != null) {
631 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700632 }
633 }
634 });
635 }
636
637 /**
638 * Callback invoked when the MTU for a given connection changes
639 * @hide
640 */
641 @Override
642 public void onConfigureMTU(String address, int mtu, int status) {
643 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700644 Log.d(TAG, "onConfigureMTU() - Device=" + address
645 + " mtu=" + mtu + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700646 }
647 if (!address.equals(mDevice.getAddress())) {
648 return;
649 }
650
651 runOrQueueCallback(new Runnable() {
652 @Override
653 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700654 final BluetoothGattCallback callback = mCallback;
655 if (callback != null) {
656 callback.onMtuChanged(BluetoothGatt.this, mtu, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700657 }
658 }
659 });
660 }
661
662 /**
663 * Callback invoked when the given connection is updated
664 * @hide
665 */
666 @Override
667 public void onConnectionUpdated(String address, int interval, int latency,
668 int timeout, int status) {
669 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700670 Log.d(TAG, "onConnectionUpdated() - Device=" + address
671 + " interval=" + interval + " latency=" + latency
672 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700673 }
674 if (!address.equals(mDevice.getAddress())) {
675 return;
676 }
677
678 runOrQueueCallback(new Runnable() {
679 @Override
680 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700681 final BluetoothGattCallback callback = mCallback;
682 if (callback != null) {
683 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
Jack Hea355e5e2017-08-22 16:06:54 -0700684 timeout, status);
685 }
686 }
687 });
688 }
689 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800690
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700691 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700692 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800693 mService = iGatt;
694 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700695 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800696 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700697 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800698 mServices = new ArrayList<BluetoothGattService>();
699
Matthew Xieddf7e472013-03-01 18:41:02 -0800700 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700701 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 }
703
704 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700705 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700706 *
707 * Application should call this method as early as possible after it is done with
708 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800709 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700710 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800711 if (DBG) Log.d(TAG, "close()");
712
713 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700714 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700715 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800716 }
717
718 /**
719 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700720 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 * @hide
722 */
723 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700724 int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700725 for (BluetoothGattService svc : mServices) {
Jack He2992cd02017-08-22 21:21:23 -0700726 if (svc.getDevice().equals(device)
Jack He2992cd02017-08-22 21:21:23 -0700727 && svc.getInstanceId() == instanceId
728 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800729 return svc;
730 }
731 }
732 return null;
733 }
734
735
736 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700737 * Returns a characteristic with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700738 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700739 * @hide
740 */
Jack Hea355e5e2017-08-22 16:06:54 -0700741 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
742 int instanceId) {
743 for (BluetoothGattService svc : mServices) {
744 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
745 if (charac.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700746 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700747 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700748 }
749 }
750 return null;
751 }
752
753 /**
754 * Returns a descriptor with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700755 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700756 * @hide
757 */
758 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700759 for (BluetoothGattService svc : mServices) {
760 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
761 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
762 if (desc.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700763 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700764 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700765 }
766 }
767 }
768 return null;
769 }
770
771 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700772 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
773 * immediately if no Handler was provided.
774 */
775 private void runOrQueueCallback(final Runnable cb) {
776 if (mHandler == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700777 try {
778 cb.run();
779 } catch (Exception ex) {
780 Log.w(TAG, "Unhandled exception in callback", ex);
781 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700782 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700783 mHandler.post(cb);
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700784 }
785 }
786
787 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800788 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800789 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700790 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800791 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800792 *
793 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
794 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800795 * @param callback GATT callback handler that will receive asynchronous callbacks.
Jack Hea355e5e2017-08-22 16:06:54 -0700796 * @return If true, the callback will be called to notify success or failure, false on immediate
797 * error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800798 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700799 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800800 if (DBG) Log.d(TAG, "registerApp()");
801 if (mService == null) return false;
802
803 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700804 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800805 UUID uuid = UUID.randomUUID();
806 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
807
808 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700809 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800810 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700811 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800812 return false;
813 }
814
815 return true;
816 }
817
818 /**
819 * Unregister the current application and callbacks.
820 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100821 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -0800822 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800823 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
824 if (mService == null || mClientIf == 0) return;
825
826 try {
827 mCallback = null;
828 mService.unregisterClient(mClientIf);
829 mClientIf = 0;
830 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700831 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800832 }
833 }
834
835 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800836 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800837 *
838 * <p>The connection may not be established right away, but will be
839 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700840 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800841 * invoked when the connection state changes as a result of this function.
842 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700843 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800844 * the remote device, or rather passively scan and finalize the connection
845 * when the remote device is in range/available. Generally, the first ever
846 * connection to a device should be direct (autoConnect set to false) and
847 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800848 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800849 *
850 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
851 *
852 * @param device Remote device to connect to
Jack Hea355e5e2017-08-22 16:06:54 -0700853 * @param autoConnect Whether to directly connect to the remote device (false) or to
854 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800855 * @return true, if the connection attempt was initiated successfully
856 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +0100857 @UnsupportedAppUsage
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700858 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700859 Handler handler) {
860 if (DBG) {
861 Log.d(TAG,
862 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
863 }
864 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800865 if (mConnState != CONN_STATE_IDLE) {
866 throw new IllegalStateException("Not idle");
867 }
868 mConnState = CONN_STATE_CONNECTING;
869 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700870
871 mAutoConnect = autoConnect;
872
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700873 if (!registerApp(callback, handler)) {
Jack Hea355e5e2017-08-22 16:06:54 -0700874 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800875 mConnState = CONN_STATE_IDLE;
876 }
877 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800878 return false;
879 }
880
Sungki Kimd35167a2016-05-19 10:18:07 -0700881 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800882 return true;
883 }
884
885 /**
886 * Disconnects an established connection, or cancels a connection attempt
887 * currently in progress.
888 *
889 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800890 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800891 public void disconnect() {
892 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800893 if (mService == null || mClientIf == 0) return;
894
895 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800896 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800897 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700898 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800899 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700900 }
901
902 /**
903 * Connect back to remote device.
904 *
905 * <p>This method is used to re-connect to a remote device after the
906 * connection has been dropped. If the device is not in range, the
907 * re-connection will be triggered once the device is back in range.
908 *
909 * @return true, if the connection attempt was initiated successfully
910 */
911 public boolean connect() {
912 try {
Jack He13f52c82017-07-05 14:55:35 -0700913 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
914 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700915 return true;
916 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700917 Log.e(TAG, "", e);
Matthew Xie33ec9842013-04-03 00:29:27 -0700918 return false;
919 }
920 }
921
922 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800923 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskicf733cd2017-09-14 08:51:44 -0700924 * recommendation, whether the PHY change will happen depends on other applications preferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800925 * local and remote controller capabilities. Controller can override these settings.
926 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700927 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800928 * if no PHY change happens. It is also triggered when remote device updates the PHY.
929 *
Jack Hea355e5e2017-08-22 16:06:54 -0700930 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
931 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
932 * BluetoothDevice#PHY_LE_CODED_MASK}.
933 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
934 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
935 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800936 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700937 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
938 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800939 */
940 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
941 try {
942 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700943 phyOptions);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800944 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700945 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800946 }
947 }
948
949 /**
950 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700951 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800952 */
953 public void readPhy() {
954 try {
955 mService.clientReadPhy(mClientIf, mDevice.getAddress());
956 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700957 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800958 }
959 }
960
961 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700962 * Return the remote bluetooth device this GATT client targets to
963 *
964 * @return remote bluetooth device
965 */
966 public BluetoothDevice getDevice() {
967 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800968 }
969
970 /**
971 * Discovers services offered by a remote device as well as their
972 * characteristics and descriptors.
973 *
974 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700975 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800976 * triggered. If the discovery was successful, the remote services can be
977 * retrieved using the {@link #getServices} function.
978 *
979 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
980 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800981 * @return true, if the remote service discovery has been started
982 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800983 public boolean discoverServices() {
984 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800985 if (mService == null || mClientIf == 0) return false;
986
987 mServices.clear();
988
989 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800990 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800991 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700992 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800993 return false;
994 }
995
996 return true;
997 }
998
999 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -07001000 * Discovers a service by UUID. This is exposed only for passing PTS tests.
1001 * It should never be used by real applications. The service is not searched
1002 * for characteristics and descriptors, or returned in any callback.
1003 *
1004 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1005 *
1006 * @return true, if the remote service discovery has been started
1007 * @hide
1008 */
1009 public boolean discoverServiceByUuid(UUID uuid) {
1010 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
1011 if (mService == null || mClientIf == 0) return false;
1012
1013 mServices.clear();
1014
1015 try {
1016 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
1017 } catch (RemoteException e) {
1018 Log.e(TAG, "", e);
1019 return false;
1020 }
1021 return true;
1022 }
1023
1024 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001025 * Returns a list of GATT services offered by the remote device.
1026 *
1027 * <p>This function requires that service discovery has been completed
1028 * for the given device.
1029 *
1030 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1031 *
Jack Hea355e5e2017-08-22 16:06:54 -07001032 * @return List of services on the remote device. Returns an empty list if service discovery has
1033 * not yet been performed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001034 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001035 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001036 List<BluetoothGattService> result =
1037 new ArrayList<BluetoothGattService>();
1038
1039 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001040 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001041 result.add(service);
1042 }
1043 }
1044
1045 return result;
1046 }
1047
1048 /**
1049 * Returns a {@link BluetoothGattService}, if the requested UUID is
1050 * supported by the remote device.
1051 *
1052 * <p>This function requires that service discovery has been completed
1053 * for the given device.
1054 *
1055 * <p>If multiple instances of the same service (as identified by UUID)
1056 * exist, the first instance of the service is returned.
1057 *
1058 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1059 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001060 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -07001061 * @return BluetoothGattService if supported, or null if the requested service is not offered by
1062 * the remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001063 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001064 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001065 for (BluetoothGattService service : mServices) {
Jack He2992cd02017-08-22 21:21:23 -07001066 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001067 return service;
1068 }
1069 }
1070
1071 return null;
1072 }
1073
1074 /**
1075 * Reads the requested characteristic from the associated remote device.
1076 *
1077 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001078 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001079 * callback.
1080 *
1081 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1082 *
1083 * @param characteristic Characteristic to read from the remote device
1084 * @return true, if the read operation was initiated successfully
1085 */
1086 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
Jack He2992cd02017-08-22 21:21:23 -07001087 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001088 return false;
1089 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001090
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001091 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001092 if (mService == null || mClientIf == 0) return false;
1093
1094 BluetoothGattService service = characteristic.getService();
1095 if (service == null) return false;
1096
1097 BluetoothDevice device = service.getDevice();
1098 if (device == null) return false;
1099
Jack Hea355e5e2017-08-22 16:06:54 -07001100 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001101 if (mDeviceBusy) return false;
1102 mDeviceBusy = true;
1103 }
1104
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001105 try {
1106 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001107 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001108 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001109 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001110 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001111 return false;
1112 }
1113
1114 return true;
1115 }
1116
1117 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001118 * Reads the characteristic using its UUID from the associated remote device.
1119 *
1120 * <p>This is an asynchronous operation. The result of the read operation
1121 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1122 * callback.
1123 *
1124 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1125 *
1126 * @param uuid UUID of characteristic to read from the remote device
1127 * @return true, if the read operation was initiated successfully
1128 * @hide
1129 */
1130 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1131 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1132 if (mService == null || mClientIf == 0) return false;
1133
Jack Hea355e5e2017-08-22 16:06:54 -07001134 synchronized (mDeviceBusy) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001135 if (mDeviceBusy) return false;
1136 mDeviceBusy = true;
1137 }
1138
1139 try {
1140 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001141 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001142 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001143 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001144 mDeviceBusy = false;
1145 return false;
1146 }
1147
1148 return true;
1149 }
1150
1151
1152 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001153 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001154 *
1155 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001156 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001157 * reporting the result of the operation.
1158 *
1159 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1160 *
1161 * @param characteristic Characteristic to write on the remote device
1162 * @return true, if the write operation was initiated successfully
1163 */
1164 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1165 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack He2992cd02017-08-22 21:21:23 -07001166 && (characteristic.getProperties()
1167 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001168 return false;
1169 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001170
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001171 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001172 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001173
1174 BluetoothGattService service = characteristic.getService();
1175 if (service == null) return false;
1176
1177 BluetoothDevice device = service.getDevice();
1178 if (device == null) return false;
1179
Jack Hea355e5e2017-08-22 16:06:54 -07001180 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001181 if (mDeviceBusy) return false;
1182 mDeviceBusy = true;
1183 }
1184
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001185 try {
1186 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001187 characteristic.getInstanceId(), characteristic.getWriteType(),
1188 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001189 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001190 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001191 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001192 return false;
1193 }
1194
1195 return true;
1196 }
1197
1198 /**
1199 * Reads the value for a given descriptor from the associated remote device.
1200 *
1201 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001202 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001203 * triggered, signaling the result of the operation.
1204 *
1205 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1206 *
1207 * @param descriptor Descriptor value to read from the remote device
1208 * @return true, if the read operation was initiated successfully
1209 */
1210 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001211 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001212 if (mService == null || mClientIf == 0) return false;
1213
1214 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1215 if (characteristic == null) return false;
1216
1217 BluetoothGattService service = characteristic.getService();
1218 if (service == null) return false;
1219
1220 BluetoothDevice device = service.getDevice();
1221 if (device == null) return false;
1222
Jack Hea355e5e2017-08-22 16:06:54 -07001223 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001224 if (mDeviceBusy) return false;
1225 mDeviceBusy = true;
1226 }
1227
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001228 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001229 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001230 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001231 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001232 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001233 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001234 return false;
1235 }
1236
1237 return true;
1238 }
1239
1240 /**
1241 * Write the value of a given descriptor to the associated remote device.
1242 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001243 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001244 * triggered to report the result of the write operation.
1245 *
1246 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1247 *
1248 * @param descriptor Descriptor to write to the associated remote device
1249 * @return true, if the write operation was initiated successfully
1250 */
1251 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001252 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001253 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001254
1255 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1256 if (characteristic == null) return false;
1257
1258 BluetoothGattService service = characteristic.getService();
1259 if (service == null) return false;
1260
1261 BluetoothDevice device = service.getDevice();
1262 if (device == null) return false;
1263
Jack Hea355e5e2017-08-22 16:06:54 -07001264 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001265 if (mDeviceBusy) return false;
1266 mDeviceBusy = true;
1267 }
1268
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001269 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001270 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001271 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001272 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001273 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001274 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001275 return false;
1276 }
1277
1278 return true;
1279 }
1280
1281 /**
1282 * Initiates a reliable write transaction for a given remote device.
1283 *
1284 * <p>Once a reliable write transaction has been initiated, all calls
1285 * to {@link #writeCharacteristic} are sent to the remote device for
1286 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001287 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001288 * in response to every {@link #writeCharacteristic} call and is responsible
1289 * for verifying if the value has been transmitted accurately.
1290 *
1291 * <p>After all characteristics have been queued up and verified,
1292 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1293 * was not written correctly, calling {@link #abortReliableWrite} will
1294 * cancel the current transaction without commiting any values on the
1295 * remote device.
1296 *
1297 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1298 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001299 * @return true, if the reliable write transaction has been initiated
1300 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001301 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001302 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001303 if (mService == null || mClientIf == 0) return false;
1304
1305 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001306 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001307 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001308 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001309 return false;
1310 }
1311
1312 return true;
1313 }
1314
1315 /**
1316 * Executes a reliable write transaction for a given remote device.
1317 *
1318 * <p>This function will commit all queued up characteristic write
1319 * operations for a given remote device.
1320 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001321 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001322 * invoked to indicate whether the transaction has been executed correctly.
1323 *
1324 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1325 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001326 * @return true, if the request to execute the transaction has been sent
1327 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001328 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001329 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001330 if (mService == null || mClientIf == 0) return false;
1331
Jack Hea355e5e2017-08-22 16:06:54 -07001332 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001333 if (mDeviceBusy) return false;
1334 mDeviceBusy = true;
1335 }
1336
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001337 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001338 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001339 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001340 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001341 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001342 return false;
1343 }
1344
1345 return true;
1346 }
1347
1348 /**
1349 * Cancels a reliable write transaction for a given device.
1350 *
1351 * <p>Calling this function will discard all queued characteristic write
1352 * operations for a given remote device.
1353 *
1354 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001355 */
John Du48f8b5d2013-08-19 12:20:37 -07001356 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001357 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001358 if (mService == null || mClientIf == 0) return;
1359
1360 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001361 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001362 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001363 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001364 }
1365 }
1366
1367 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001368 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001369 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001370 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001371 public void abortReliableWrite(BluetoothDevice mDevice) {
1372 abortReliableWrite();
1373 }
1374
1375 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001376 * Enable or disable notifications/indications for a given characteristic.
1377 *
1378 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001379 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001380 * triggered if the remote device indicates that the given characteristic
1381 * has changed.
1382 *
1383 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1384 *
1385 * @param characteristic The characteristic for which to enable notifications
1386 * @param enable Set to true to enable notifications/indications
1387 * @return true, if the requested notification status was set successfully
1388 */
1389 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001390 boolean enable) {
1391 if (DBG) {
1392 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1393 + " enable: " + enable);
1394 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001395 if (mService == null || mClientIf == 0) return false;
1396
1397 BluetoothGattService service = characteristic.getService();
1398 if (service == null) return false;
1399
1400 BluetoothDevice device = service.getDevice();
1401 if (device == null) return false;
1402
1403 try {
1404 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001405 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001406 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001407 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001408 return false;
1409 }
1410
1411 return true;
1412 }
1413
1414 /**
1415 * Clears the internal cache and forces a refresh of the services from the
1416 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001417 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001418 * @hide
1419 */
Mathew Inwood4dc66d32018-08-01 15:07:20 +01001420 @UnsupportedAppUsage
Matthew Xieddf7e472013-03-01 18:41:02 -08001421 public boolean refresh() {
1422 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001423 if (mService == null || mClientIf == 0) return false;
1424
1425 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001426 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001427 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001428 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001429 return false;
1430 }
1431
1432 return true;
1433 }
1434
1435 /**
1436 * Read the RSSI for a connected remote device.
1437 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001438 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001439 * invoked when the RSSI value has been read.
1440 *
1441 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1442 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001443 * @return true, if the RSSI value has been requested successfully
1444 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001445 public boolean readRemoteRssi() {
1446 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001447 if (mService == null || mClientIf == 0) return false;
1448
1449 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001450 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001451 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001452 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001453 return false;
1454 }
1455
1456 return true;
1457 }
1458
1459 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001460 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001461 *
1462 * <p>When performing a write request operation (write without response),
1463 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001464 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001465 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001466 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001467 * whether this operation was successful.
1468 *
1469 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1470 *
1471 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001472 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001473 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001474 if (DBG) {
1475 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1476 + " mtu: " + mtu);
1477 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001478 if (mService == null || mClientIf == 0) return false;
1479
1480 try {
1481 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1482 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001483 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001484 return false;
1485 }
1486
1487 return true;
1488 }
1489
1490 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001491 * Request a connection parameter update.
1492 *
1493 * <p>This function will send a connection parameter update request to the
1494 * remote device.
1495 *
Jack Hea355e5e2017-08-22 16:06:54 -07001496 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1497 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1498 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1499 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001500 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001501 public boolean requestConnectionPriority(int connectionPriority) {
Jack He2992cd02017-08-22 21:21:23 -07001502 if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1503 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001504 throw new IllegalArgumentException("connectionPriority not within valid range");
1505 }
1506
Andre Eisenbach4072da02014-08-19 17:58:55 -07001507 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001508 if (mService == null || mClientIf == 0) return false;
1509
1510 try {
1511 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1512 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001513 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001514 return false;
1515 }
1516
1517 return true;
1518 }
1519
1520 /**
Stanley Tng6da1dda2018-01-04 15:42:25 -08001521 * Request an LE connection parameter update.
1522 *
1523 * <p>This function will send an LE connection parameters update request to the remote device.
1524 *
1525 * @return true, if the request is send to the Bluetooth stack.
1526 * @hide
1527 */
Stanley Tng148dd5b2018-03-19 12:28:56 -07001528 public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
1529 int slaveLatency, int supervisionTimeout,
1530 int minConnectionEventLen, int maxConnectionEventLen) {
Stanley Tng6da1dda2018-01-04 15:42:25 -08001531 if (DBG) {
1532 Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
Stanley Tng148dd5b2018-03-19 12:28:56 -07001533 + ")" + (1.25 * minConnectionInterval)
1534 + "msec, max=(" + maxConnectionInterval + ")"
Stanley Tng6da1dda2018-01-04 15:42:25 -08001535 + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
Stanley Tng148dd5b2018-03-19 12:28:56 -07001536 + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
1537 + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001538 }
1539 if (mService == null || mClientIf == 0) return false;
1540
1541 try {
1542 mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
Stanley Tng148dd5b2018-03-19 12:28:56 -07001543 minConnectionInterval, maxConnectionInterval,
1544 slaveLatency, supervisionTimeout,
1545 minConnectionEventLen, maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001546 } catch (RemoteException e) {
1547 Log.e(TAG, "", e);
1548 return false;
1549 }
1550
1551 return true;
1552 }
1553
1554 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001555 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1556 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001557 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001558 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001559 */
1560 @Override
1561 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001562 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001563 }
1564
1565 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001566 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1567 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001568 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001569 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001570 */
1571 @Override
1572 public List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -07001573 throw new UnsupportedOperationException(
1574 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001575 }
1576
1577 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001578 * Not supported - please use
1579 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1580 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001581 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001582 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001583 */
1584 @Override
1585 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -07001586 throw new UnsupportedOperationException(
1587 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001588 }
1589}