blob: 71edc8a32c9074ad51ba2418fdb9c2ba653547c7 [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
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070019import android.os.Handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080020import android.os.ParcelUuid;
21import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.util.Log;
23
24import java.util.ArrayList;
25import java.util.List;
26import java.util.UUID;
27
28/**
Matthew Xieddf7e472013-03-01 18:41:02 -080029 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080030 *
Matthew Xieddf7e472013-03-01 18:41:02 -080031 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080032 * with Bluetooth Smart or Smart Ready devices.
33 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070034 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070035 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080036 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
37 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080038 */
39public final class BluetoothGatt implements BluetoothProfile {
40 private static final String TAG = "BluetoothGatt";
41 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070042 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080043
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044 private IBluetoothGatt mService;
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -070045 private volatile BluetoothGattCallback mCallback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070046 private Handler mHandler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080047 private int mClientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -080048 private BluetoothDevice mDevice;
49 private boolean mAutoConnect;
Jacky Cheung3854e222016-10-20 13:55:21 -070050 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080051 private int mConnState;
52 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070053 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070054 private int mTransport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080055 private int mPhy;
Jack He13f52c82017-07-05 14:55:35 -070056 private boolean mOpportunistic;
Matthew Xieddf7e472013-03-01 18:41:02 -080057
Jacky Cheung3854e222016-10-20 13:55:21 -070058 private static final int AUTH_RETRY_STATE_IDLE = 0;
59 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
60 private static final int AUTH_RETRY_STATE_MITM = 2;
61
Matthew Xieddf7e472013-03-01 18:41:02 -080062 private static final int CONN_STATE_IDLE = 0;
63 private static final int CONN_STATE_CONNECTING = 1;
64 private static final int CONN_STATE_CONNECTED = 2;
65 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070066 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080067
68 private List<BluetoothGattService> mServices;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_SUCCESS = 0;
72
Matthew Xieddf7e472013-03-01 18:41:02 -080073 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080074 public static final int GATT_READ_NOT_PERMITTED = 0x2;
75
Matthew Xieddf7e472013-03-01 18:41:02 -080076 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080077 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
78
79 /** Insufficient authentication for a given operation */
80 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
81
82 /** The given request is not supported */
83 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
84
85 /** Insufficient encryption for a given operation */
86 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
87
88 /** A read or write operation was requested with an invalid offset */
89 public static final int GATT_INVALID_OFFSET = 0x7;
90
91 /** A write operation exceeds the maximum length of the attribute */
92 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
93
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070094 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070095 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
96
Matthew Xie90ca8072013-05-28 21:06:50 +000097 /** A GATT operation failed, errors other than the above */
98 public static final int GATT_FAILURE = 0x101;
99
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800100 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700101 * Connection paramter update - Use the connection paramters recommended by the
102 * Bluetooth SIG. This is the default value if no connection parameter update
103 * is requested.
104 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700105 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700106
107 /**
108 * Connection paramter update - Request a high priority, low latency connection.
109 * An application should only request high priority connection paramters to transfer
110 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700111 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112 * to reduce energy use.
113 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700114 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700115
116 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700117 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700118
119 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800120 * No authentication required.
Jack Hea355e5e2017-08-22 16:06:54 -0700121 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800122 * @hide
123 */
124 /*package*/ static final int AUTHENTICATION_NONE = 0;
125
126 /**
127 * Authentication requested; no man-in-the-middle protection required.
Jack Hea355e5e2017-08-22 16:06:54 -0700128 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800129 * @hide
130 */
131 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
132
133 /**
134 * Authentication with man-in-the-middle protection requested.
Jack Hea355e5e2017-08-22 16:06:54 -0700135 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800136 * @hide
137 */
138 /*package*/ static final int AUTHENTICATION_MITM = 2;
139
140 /**
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700141 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800142 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -0700143 private final IBluetoothGattCallback mBluetoothGattCallback =
Jack Hea355e5e2017-08-22 16:06:54 -0700144 new IBluetoothGattCallback.Stub() {
145 /**
146 * Application interface registered - app is ready to go
147 * @hide
148 */
149 @Override
150 public void onClientRegistered(int status, int clientIf) {
151 if (DBG) {
152 Log.d(TAG, "onClientRegistered() - status=" + status
153 + " clientIf=" + clientIf);
154 }
155 if (VDBG) {
156 synchronized (mStateLock) {
157 if (mConnState != CONN_STATE_CONNECTING) {
158 Log.e(TAG, "Bad connection state: " + mConnState);
159 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800160 }
161 }
Jack Hea355e5e2017-08-22 16:06:54 -0700162 mClientIf = clientIf;
163 if (status != GATT_SUCCESS) {
164 runOrQueueCallback(new Runnable() {
165 @Override
166 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700167 final BluetoothGattCallback callback = mCallback;
168 if (callback != null) {
169 callback.onConnectionStateChange(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700170 GATT_FAILURE,
171 BluetoothProfile.STATE_DISCONNECTED);
172 }
173 }
174 });
175
176 synchronized (mStateLock) {
177 mConnState = CONN_STATE_IDLE;
178 }
179 return;
180 }
181 try {
182 mService.clientConnect(mClientIf, mDevice.getAddress(),
183 !mAutoConnect, mTransport, mOpportunistic,
184 mPhy); // autoConnect is inverse of "isDirect"
185 } catch (RemoteException e) {
186 Log.e(TAG, "", e);
187 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800188 }
Jack Hea355e5e2017-08-22 16:06:54 -0700189
190 /**
191 * Phy update callback
192 * @hide
193 */
194 @Override
195 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
196 if (DBG) {
197 Log.d(TAG, "onPhyUpdate() - status=" + status
198 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
199 }
200 if (!address.equals(mDevice.getAddress())) {
201 return;
202 }
203
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700204 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700205 @Override
206 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700207 final BluetoothGattCallback callback = mCallback;
208 if (callback != null) {
209 callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700210 }
211 }
212 });
213 }
214
215 /**
216 * Phy read callback
217 * @hide
218 */
219 @Override
220 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
221 if (DBG) {
222 Log.d(TAG, "onPhyRead() - status=" + status
223 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
224 }
225 if (!address.equals(mDevice.getAddress())) {
226 return;
227 }
228
229 runOrQueueCallback(new Runnable() {
230 @Override
231 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700232 final BluetoothGattCallback callback = mCallback;
233 if (callback != null) {
234 callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700235 }
236 }
237 });
238 }
239
240 /**
241 * Client connection state changed
242 * @hide
243 */
244 @Override
245 public void onClientConnectionState(int status, int clientIf,
246 boolean connected, String address) {
247 if (DBG) {
248 Log.d(TAG, "onClientConnectionState() - status=" + status
249 + " clientIf=" + clientIf + " device=" + address);
250 }
251 if (!address.equals(mDevice.getAddress())) {
252 return;
253 }
254 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
255 BluetoothProfile.STATE_DISCONNECTED;
256
257 runOrQueueCallback(new Runnable() {
258 @Override
259 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700260 final BluetoothGattCallback callback = mCallback;
261 if (callback != null) {
262 callback.onConnectionStateChange(BluetoothGatt.this, status,
Jack Hea355e5e2017-08-22 16:06:54 -0700263 profileState);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700264 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700265 }
266 });
267
Jack Hea355e5e2017-08-22 16:06:54 -0700268 synchronized (mStateLock) {
269 if (connected) {
270 mConnState = CONN_STATE_CONNECTED;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800271 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700272 mConnState = CONN_STATE_IDLE;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800273 }
274 }
Jack Hea355e5e2017-08-22 16:06:54 -0700275
276 synchronized (mDeviceBusy) {
277 mDeviceBusy = false;
278 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800279 }
280
Jack Hea355e5e2017-08-22 16:06:54 -0700281 /**
282 * Remote search has been completed.
283 * The internal object structure should now reflect the state
284 * of the remote device database. Let the application know that
285 * we are done at this point.
286 * @hide
287 */
288 @Override
289 public void onSearchComplete(String address, List<BluetoothGattService> services,
290 int status) {
291 if (DBG) {
292 Log.d(TAG,
293 "onSearchComplete() = Device=" + address + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700294 }
Jack Hea355e5e2017-08-22 16:06:54 -0700295 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800296 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800297 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800298
Jack Hea355e5e2017-08-22 16:06:54 -0700299 for (BluetoothGattService s : services) {
300 //services we receive don't have device set properly.
301 s.setDevice(mDevice);
302 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800303
Jack Hea355e5e2017-08-22 16:06:54 -0700304 mServices.addAll(services);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800305
Jack Hea355e5e2017-08-22 16:06:54 -0700306 // Fix references to included services, as they doesn't point to right objects.
307 for (BluetoothGattService fixedService : mServices) {
308 ArrayList<BluetoothGattService> includedServices =
309 new ArrayList(fixedService.getIncludedServices());
310 fixedService.getIncludedServices().clear();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800311
Jack Hea355e5e2017-08-22 16:06:54 -0700312 for (BluetoothGattService brokenRef : includedServices) {
313 BluetoothGattService includedService = getService(mDevice,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700314 brokenRef.getUuid(), brokenRef.getInstanceId());
Jack Hea355e5e2017-08-22 16:06:54 -0700315 if (includedService != null) {
316 fixedService.addIncludedService(includedService);
317 } else {
318 Log.e(TAG, "Broken GATT database: can't find included service.");
319 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700320 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700321 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800322
Jack Hea355e5e2017-08-22 16:06:54 -0700323 runOrQueueCallback(new Runnable() {
324 @Override
325 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700326 final BluetoothGattCallback callback = mCallback;
327 if (callback != null) {
328 callback.onServicesDiscovered(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700329 }
330 }
331 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800332 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700333
Jack Hea355e5e2017-08-22 16:06:54 -0700334 /**
335 * Remote characteristic has been read.
336 * Updates the internal value.
337 * @hide
338 */
339 @Override
340 public void onCharacteristicRead(String address, int status, int handle,
341 byte[] value) {
342 if (VDBG) {
343 Log.d(TAG, "onCharacteristicRead() - Device=" + address
344 + " handle=" + handle + " Status=" + status);
345 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700346
Jack Hea355e5e2017-08-22 16:06:54 -0700347 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800348 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800349 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800350
Jack Hea355e5e2017-08-22 16:06:54 -0700351 synchronized (mDeviceBusy) {
352 mDeviceBusy = false;
353 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800354
Jack Hea355e5e2017-08-22 16:06:54 -0700355 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
356 || status == GATT_INSUFFICIENT_ENCRYPTION)
357 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
358 try {
Jack He2992cd02017-08-22 21:21:23 -0700359 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
360 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700361 mService.readCharacteristic(mClientIf, address, handle, authReq);
362 mAuthRetryState++;
363 return;
364 } catch (RemoteException e) {
365 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700366 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700367 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800368
Jack Hea355e5e2017-08-22 16:06:54 -0700369 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800370
Jack Hea355e5e2017-08-22 16:06:54 -0700371 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
372 handle);
373 if (characteristic == null) {
374 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700375 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800376 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800377
Jack Hea355e5e2017-08-22 16:06:54 -0700378 runOrQueueCallback(new Runnable() {
379 @Override
380 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700381 final BluetoothGattCallback callback = mCallback;
382 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700383 if (status == 0) characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700384 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700385 status);
386 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700387 }
Jack Hea355e5e2017-08-22 16:06:54 -0700388 });
389 }
390
391 /**
392 * Characteristic has been written to the remote device.
393 * Let the app know how we did...
394 * @hide
395 */
396 @Override
397 public void onCharacteristicWrite(String address, int status, int handle) {
398 if (VDBG) {
399 Log.d(TAG, "onCharacteristicWrite() - Device=" + address
400 + " handle=" + handle + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700401 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800402
Jack Hea355e5e2017-08-22 16:06:54 -0700403 if (!address.equals(mDevice.getAddress())) {
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700404 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800405 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800406
Jack Hea355e5e2017-08-22 16:06:54 -0700407 synchronized (mDeviceBusy) {
408 mDeviceBusy = false;
409 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800410
Jack Hea355e5e2017-08-22 16:06:54 -0700411 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
412 handle);
413 if (characteristic == null) return;
414
415 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
416 || status == GATT_INSUFFICIENT_ENCRYPTION)
417 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
418 try {
Jack He2992cd02017-08-22 21:21:23 -0700419 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
420 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700421 mService.writeCharacteristic(mClientIf, address, handle,
422 characteristic.getWriteType(), authReq,
423 characteristic.getValue());
424 mAuthRetryState++;
425 return;
426 } catch (RemoteException e) {
427 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700428 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700429 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800430
Jack Hea355e5e2017-08-22 16:06:54 -0700431 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
432
433 runOrQueueCallback(new Runnable() {
434 @Override
435 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700436 final BluetoothGattCallback callback = mCallback;
437 if (callback != null) {
438 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700439 status);
440 }
441 }
442 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800443 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700444
Jack Hea355e5e2017-08-22 16:06:54 -0700445 /**
446 * Remote characteristic has been updated.
447 * Updates the internal value.
448 * @hide
449 */
450 @Override
451 public void onNotify(String address, int handle, byte[] value) {
452 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
453
454 if (!address.equals(mDevice.getAddress())) {
455 return;
456 }
457
458 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
459 handle);
460 if (characteristic == null) return;
461
Jack Hea355e5e2017-08-22 16:06:54 -0700462 runOrQueueCallback(new Runnable() {
463 @Override
464 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700465 final BluetoothGattCallback callback = mCallback;
466 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700467 characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700468 callback.onCharacteristicChanged(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700469 characteristic);
470 }
471 }
472 });
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700473 }
474
Jack Hea355e5e2017-08-22 16:06:54 -0700475 /**
476 * Descriptor has been read.
477 * @hide
478 */
479 @Override
480 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
481 if (VDBG) {
482 Log.d(TAG,
483 "onDescriptorRead() - Device=" + address + " handle=" + handle);
484 }
485
486 if (!address.equals(mDevice.getAddress())) {
487 return;
488 }
489
490 synchronized (mDeviceBusy) {
491 mDeviceBusy = false;
492 }
493
494 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
495 if (descriptor == null) return;
496
Jack Hea355e5e2017-08-22 16:06:54 -0700497
498 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
499 || status == GATT_INSUFFICIENT_ENCRYPTION)
500 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
501 try {
Jack He2992cd02017-08-22 21:21:23 -0700502 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
503 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700504 mService.readDescriptor(mClientIf, address, handle, authReq);
505 mAuthRetryState++;
506 return;
507 } catch (RemoteException e) {
508 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700509 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700510 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800511
Jack Hea355e5e2017-08-22 16:06:54 -0700512 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
513
514 runOrQueueCallback(new Runnable() {
515 @Override
516 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700517 final BluetoothGattCallback callback = mCallback;
518 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700519 if (status == 0) descriptor.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700520 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700521 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700522 }
Jack Hea355e5e2017-08-22 16:06:54 -0700523 });
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700524 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700525
Jack Hea355e5e2017-08-22 16:06:54 -0700526 /**
527 * Descriptor write operation complete.
528 * @hide
529 */
530 @Override
531 public void onDescriptorWrite(String address, int status, int handle) {
532 if (VDBG) {
533 Log.d(TAG,
534 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
535 }
536
537 if (!address.equals(mDevice.getAddress())) {
538 return;
539 }
540
541 synchronized (mDeviceBusy) {
542 mDeviceBusy = false;
543 }
544
545 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
546 if (descriptor == null) return;
547
548 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
549 || status == GATT_INSUFFICIENT_ENCRYPTION)
550 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
551 try {
Jack He2992cd02017-08-22 21:21:23 -0700552 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
553 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700554 mService.writeDescriptor(mClientIf, address, handle,
555 authReq, descriptor.getValue());
556 mAuthRetryState++;
557 return;
558 } catch (RemoteException e) {
559 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700560 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700561 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700562
Jack Hea355e5e2017-08-22 16:06:54 -0700563 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
564
565 runOrQueueCallback(new Runnable() {
566 @Override
567 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700568 final BluetoothGattCallback callback = mCallback;
569 if (callback != null) {
570 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700571 }
572 }
573 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700574 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700575
Jack Hea355e5e2017-08-22 16:06:54 -0700576 /**
577 * Prepared write transaction completed (or aborted)
578 * @hide
579 */
580 @Override
581 public void onExecuteWrite(String address, int status) {
582 if (VDBG) {
583 Log.d(TAG, "onExecuteWrite() - Device=" + address
584 + " status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700585 }
Jack Hea355e5e2017-08-22 16:06:54 -0700586 if (!address.equals(mDevice.getAddress())) {
587 return;
588 }
589
590 synchronized (mDeviceBusy) {
591 mDeviceBusy = false;
592 }
593
594 runOrQueueCallback(new Runnable() {
595 @Override
596 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700597 final BluetoothGattCallback callback = mCallback;
598 if (callback != null) {
599 callback.onReliableWriteCompleted(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700600 }
601 }
602 });
603 }
604
605 /**
606 * Remote device RSSI has been read
607 * @hide
608 */
609 @Override
610 public void onReadRemoteRssi(String address, int rssi, int status) {
611 if (VDBG) {
Jack He2992cd02017-08-22 21:21:23 -0700612 Log.d(TAG, "onReadRemoteRssi() - Device=" + address
613 + " rssi=" + rssi + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700614 }
615 if (!address.equals(mDevice.getAddress())) {
616 return;
617 }
618 runOrQueueCallback(new Runnable() {
619 @Override
620 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700621 final BluetoothGattCallback callback = mCallback;
622 if (callback != null) {
623 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700624 }
625 }
626 });
627 }
628
629 /**
630 * Callback invoked when the MTU for a given connection changes
631 * @hide
632 */
633 @Override
634 public void onConfigureMTU(String address, int mtu, int status) {
635 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700636 Log.d(TAG, "onConfigureMTU() - Device=" + address
637 + " mtu=" + mtu + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700638 }
639 if (!address.equals(mDevice.getAddress())) {
640 return;
641 }
642
643 runOrQueueCallback(new Runnable() {
644 @Override
645 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700646 final BluetoothGattCallback callback = mCallback;
647 if (callback != null) {
648 callback.onMtuChanged(BluetoothGatt.this, mtu, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700649 }
650 }
651 });
652 }
653
654 /**
655 * Callback invoked when the given connection is updated
656 * @hide
657 */
658 @Override
659 public void onConnectionUpdated(String address, int interval, int latency,
660 int timeout, int status) {
661 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700662 Log.d(TAG, "onConnectionUpdated() - Device=" + address
663 + " interval=" + interval + " latency=" + latency
664 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700665 }
666 if (!address.equals(mDevice.getAddress())) {
667 return;
668 }
669
670 runOrQueueCallback(new Runnable() {
671 @Override
672 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700673 final BluetoothGattCallback callback = mCallback;
674 if (callback != null) {
675 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
Jack Hea355e5e2017-08-22 16:06:54 -0700676 timeout, status);
677 }
678 }
679 });
680 }
681 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700683 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700684 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800685 mService = iGatt;
686 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700687 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800688 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700689 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800690 mServices = new ArrayList<BluetoothGattService>();
691
Matthew Xieddf7e472013-03-01 18:41:02 -0800692 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700693 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800694 }
695
696 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700697 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700698 *
699 * Application should call this method as early as possible after it is done with
700 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800701 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700702 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800703 if (DBG) Log.d(TAG, "close()");
704
705 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700706 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700707 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800708 }
709
710 /**
711 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700712 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800713 * @hide
714 */
715 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
Jakub Pawlowski8dde5e12017-09-14 11:54:59 -0700716 int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700717 for (BluetoothGattService svc : mServices) {
Jack He2992cd02017-08-22 21:21:23 -0700718 if (svc.getDevice().equals(device)
Jack He2992cd02017-08-22 21:21:23 -0700719 && svc.getInstanceId() == instanceId
720 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 return svc;
722 }
723 }
724 return null;
725 }
726
727
728 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700729 * Returns a characteristic with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700730 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700731 * @hide
732 */
Jack Hea355e5e2017-08-22 16:06:54 -0700733 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
734 int instanceId) {
735 for (BluetoothGattService svc : mServices) {
736 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
737 if (charac.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700738 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700739 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700740 }
741 }
742 return null;
743 }
744
745 /**
746 * Returns a descriptor with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700747 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700748 * @hide
749 */
750 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700751 for (BluetoothGattService svc : mServices) {
752 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
753 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
754 if (desc.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700755 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700756 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700757 }
758 }
759 }
760 return null;
761 }
762
763 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700764 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
765 * immediately if no Handler was provided.
766 */
767 private void runOrQueueCallback(final Runnable cb) {
768 if (mHandler == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700769 try {
770 cb.run();
771 } catch (Exception ex) {
772 Log.w(TAG, "Unhandled exception in callback", ex);
773 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700774 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700775 mHandler.post(cb);
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700776 }
777 }
778
779 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800780 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800781 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700782 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800783 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800784 *
785 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
786 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800787 * @param callback GATT callback handler that will receive asynchronous callbacks.
Jack Hea355e5e2017-08-22 16:06:54 -0700788 * @return If true, the callback will be called to notify success or failure, false on immediate
789 * error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700791 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800792 if (DBG) Log.d(TAG, "registerApp()");
793 if (mService == null) return false;
794
795 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700796 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800797 UUID uuid = UUID.randomUUID();
798 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
799
800 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700801 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800802 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700803 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800804 return false;
805 }
806
807 return true;
808 }
809
810 /**
811 * Unregister the current application and callbacks.
812 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800813 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800814 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
815 if (mService == null || mClientIf == 0) return;
816
817 try {
818 mCallback = null;
819 mService.unregisterClient(mClientIf);
820 mClientIf = 0;
821 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700822 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800823 }
824 }
825
826 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800827 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800828 *
829 * <p>The connection may not be established right away, but will be
830 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700831 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800832 * invoked when the connection state changes as a result of this function.
833 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700834 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800835 * the remote device, or rather passively scan and finalize the connection
836 * when the remote device is in range/available. Generally, the first ever
837 * connection to a device should be direct (autoConnect set to false) and
838 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800839 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800840 *
841 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
842 *
843 * @param device Remote device to connect to
Jack Hea355e5e2017-08-22 16:06:54 -0700844 * @param autoConnect Whether to directly connect to the remote device (false) or to
845 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800846 * @return true, if the connection attempt was initiated successfully
847 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700848 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700849 Handler handler) {
850 if (DBG) {
851 Log.d(TAG,
852 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
853 }
854 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800855 if (mConnState != CONN_STATE_IDLE) {
856 throw new IllegalStateException("Not idle");
857 }
858 mConnState = CONN_STATE_CONNECTING;
859 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700860
861 mAutoConnect = autoConnect;
862
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700863 if (!registerApp(callback, handler)) {
Jack Hea355e5e2017-08-22 16:06:54 -0700864 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800865 mConnState = CONN_STATE_IDLE;
866 }
867 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800868 return false;
869 }
870
Sungki Kimd35167a2016-05-19 10:18:07 -0700871 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800872 return true;
873 }
874
875 /**
876 * Disconnects an established connection, or cancels a connection attempt
877 * currently in progress.
878 *
879 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800880 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800881 public void disconnect() {
882 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800883 if (mService == null || mClientIf == 0) return;
884
885 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800886 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800887 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700888 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800889 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700890 }
891
892 /**
893 * Connect back to remote device.
894 *
895 * <p>This method is used to re-connect to a remote device after the
896 * connection has been dropped. If the device is not in range, the
897 * re-connection will be triggered once the device is back in range.
898 *
899 * @return true, if the connection attempt was initiated successfully
900 */
901 public boolean connect() {
902 try {
Jack He13f52c82017-07-05 14:55:35 -0700903 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
904 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700905 return true;
906 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700907 Log.e(TAG, "", e);
Matthew Xie33ec9842013-04-03 00:29:27 -0700908 return false;
909 }
910 }
911
912 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800913 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskicf733cd2017-09-14 08:51:44 -0700914 * recommendation, whether the PHY change will happen depends on other applications preferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800915 * local and remote controller capabilities. Controller can override these settings.
916 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700917 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800918 * if no PHY change happens. It is also triggered when remote device updates the PHY.
919 *
Jack Hea355e5e2017-08-22 16:06:54 -0700920 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
921 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
922 * BluetoothDevice#PHY_LE_CODED_MASK}.
923 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
924 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
925 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800926 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700927 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
928 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800929 */
930 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
931 try {
932 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700933 phyOptions);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800934 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700935 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800936 }
937 }
938
939 /**
940 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700941 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800942 */
943 public void readPhy() {
944 try {
945 mService.clientReadPhy(mClientIf, mDevice.getAddress());
946 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700947 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800948 }
949 }
950
951 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700952 * Return the remote bluetooth device this GATT client targets to
953 *
954 * @return remote bluetooth device
955 */
956 public BluetoothDevice getDevice() {
957 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800958 }
959
960 /**
961 * Discovers services offered by a remote device as well as their
962 * characteristics and descriptors.
963 *
964 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700965 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800966 * triggered. If the discovery was successful, the remote services can be
967 * retrieved using the {@link #getServices} function.
968 *
969 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
970 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800971 * @return true, if the remote service discovery has been started
972 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800973 public boolean discoverServices() {
974 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800975 if (mService == null || mClientIf == 0) return false;
976
977 mServices.clear();
978
979 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800980 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800981 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700982 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800983 return false;
984 }
985
986 return true;
987 }
988
989 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -0700990 * Discovers a service by UUID. This is exposed only for passing PTS tests.
991 * It should never be used by real applications. The service is not searched
992 * for characteristics and descriptors, or returned in any callback.
993 *
994 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
995 *
996 * @return true, if the remote service discovery has been started
997 * @hide
998 */
999 public boolean discoverServiceByUuid(UUID uuid) {
1000 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
1001 if (mService == null || mClientIf == 0) return false;
1002
1003 mServices.clear();
1004
1005 try {
1006 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
1007 } catch (RemoteException e) {
1008 Log.e(TAG, "", e);
1009 return false;
1010 }
1011 return true;
1012 }
1013
1014 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 * Returns a list of GATT services offered by the remote device.
1016 *
1017 * <p>This function requires that service discovery has been completed
1018 * for the given device.
1019 *
1020 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1021 *
Jack Hea355e5e2017-08-22 16:06:54 -07001022 * @return List of services on the remote device. Returns an empty list if service discovery has
1023 * not yet been performed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001024 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001025 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001026 List<BluetoothGattService> result =
1027 new ArrayList<BluetoothGattService>();
1028
1029 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001030 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001031 result.add(service);
1032 }
1033 }
1034
1035 return result;
1036 }
1037
1038 /**
1039 * Returns a {@link BluetoothGattService}, if the requested UUID is
1040 * supported by the remote device.
1041 *
1042 * <p>This function requires that service discovery has been completed
1043 * for the given device.
1044 *
1045 * <p>If multiple instances of the same service (as identified by UUID)
1046 * exist, the first instance of the service is returned.
1047 *
1048 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1049 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001050 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -07001051 * @return BluetoothGattService if supported, or null if the requested service is not offered by
1052 * the remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001053 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001054 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001055 for (BluetoothGattService service : mServices) {
Jack He2992cd02017-08-22 21:21:23 -07001056 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001057 return service;
1058 }
1059 }
1060
1061 return null;
1062 }
1063
1064 /**
1065 * Reads the requested characteristic from the associated remote device.
1066 *
1067 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001068 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001069 * callback.
1070 *
1071 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1072 *
1073 * @param characteristic Characteristic to read from the remote device
1074 * @return true, if the read operation was initiated successfully
1075 */
1076 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
Jack He2992cd02017-08-22 21:21:23 -07001077 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001078 return false;
1079 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001080
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001081 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001082 if (mService == null || mClientIf == 0) return false;
1083
1084 BluetoothGattService service = characteristic.getService();
1085 if (service == null) return false;
1086
1087 BluetoothDevice device = service.getDevice();
1088 if (device == null) return false;
1089
Jack Hea355e5e2017-08-22 16:06:54 -07001090 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001091 if (mDeviceBusy) return false;
1092 mDeviceBusy = true;
1093 }
1094
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001095 try {
1096 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001097 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001098 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001099 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001100 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001101 return false;
1102 }
1103
1104 return true;
1105 }
1106
1107 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001108 * Reads the characteristic using its UUID from the associated remote device.
1109 *
1110 * <p>This is an asynchronous operation. The result of the read operation
1111 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1112 * callback.
1113 *
1114 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1115 *
1116 * @param uuid UUID of characteristic to read from the remote device
1117 * @return true, if the read operation was initiated successfully
1118 * @hide
1119 */
1120 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1121 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1122 if (mService == null || mClientIf == 0) return false;
1123
Jack Hea355e5e2017-08-22 16:06:54 -07001124 synchronized (mDeviceBusy) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001125 if (mDeviceBusy) return false;
1126 mDeviceBusy = true;
1127 }
1128
1129 try {
1130 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001131 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001132 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001133 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001134 mDeviceBusy = false;
1135 return false;
1136 }
1137
1138 return true;
1139 }
1140
1141
1142 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001143 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001144 *
1145 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001146 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001147 * reporting the result of the operation.
1148 *
1149 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1150 *
1151 * @param characteristic Characteristic to write on the remote device
1152 * @return true, if the write operation was initiated successfully
1153 */
1154 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1155 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack He2992cd02017-08-22 21:21:23 -07001156 && (characteristic.getProperties()
1157 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001158 return false;
1159 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001160
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001161 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001162 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001163
1164 BluetoothGattService service = characteristic.getService();
1165 if (service == null) return false;
1166
1167 BluetoothDevice device = service.getDevice();
1168 if (device == null) return false;
1169
Jack Hea355e5e2017-08-22 16:06:54 -07001170 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001171 if (mDeviceBusy) return false;
1172 mDeviceBusy = true;
1173 }
1174
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001175 try {
1176 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001177 characteristic.getInstanceId(), characteristic.getWriteType(),
1178 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001179 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001180 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001181 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001182 return false;
1183 }
1184
1185 return true;
1186 }
1187
1188 /**
1189 * Reads the value for a given descriptor from the associated remote device.
1190 *
1191 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001192 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001193 * triggered, signaling the result of the operation.
1194 *
1195 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1196 *
1197 * @param descriptor Descriptor value to read from the remote device
1198 * @return true, if the read operation was initiated successfully
1199 */
1200 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001201 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001202 if (mService == null || mClientIf == 0) return false;
1203
1204 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1205 if (characteristic == null) return false;
1206
1207 BluetoothGattService service = characteristic.getService();
1208 if (service == null) return false;
1209
1210 BluetoothDevice device = service.getDevice();
1211 if (device == null) return false;
1212
Jack Hea355e5e2017-08-22 16:06:54 -07001213 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001214 if (mDeviceBusy) return false;
1215 mDeviceBusy = true;
1216 }
1217
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001218 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001219 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001220 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001221 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001222 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001223 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001224 return false;
1225 }
1226
1227 return true;
1228 }
1229
1230 /**
1231 * Write the value of a given descriptor to the associated remote device.
1232 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001233 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001234 * triggered to report the result of the write operation.
1235 *
1236 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1237 *
1238 * @param descriptor Descriptor to write to the associated remote device
1239 * @return true, if the write operation was initiated successfully
1240 */
1241 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001242 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001243 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001244
1245 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1246 if (characteristic == null) return false;
1247
1248 BluetoothGattService service = characteristic.getService();
1249 if (service == null) return false;
1250
1251 BluetoothDevice device = service.getDevice();
1252 if (device == null) return false;
1253
Jack Hea355e5e2017-08-22 16:06:54 -07001254 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001255 if (mDeviceBusy) return false;
1256 mDeviceBusy = true;
1257 }
1258
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001259 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001260 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001261 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001262 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001263 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001264 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001265 return false;
1266 }
1267
1268 return true;
1269 }
1270
1271 /**
1272 * Initiates a reliable write transaction for a given remote device.
1273 *
1274 * <p>Once a reliable write transaction has been initiated, all calls
1275 * to {@link #writeCharacteristic} are sent to the remote device for
1276 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001277 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001278 * in response to every {@link #writeCharacteristic} call and is responsible
1279 * for verifying if the value has been transmitted accurately.
1280 *
1281 * <p>After all characteristics have been queued up and verified,
1282 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1283 * was not written correctly, calling {@link #abortReliableWrite} will
1284 * cancel the current transaction without commiting any values on the
1285 * remote device.
1286 *
1287 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1288 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001289 * @return true, if the reliable write transaction has been initiated
1290 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001291 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001292 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001293 if (mService == null || mClientIf == 0) return false;
1294
1295 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001296 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001297 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001298 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001299 return false;
1300 }
1301
1302 return true;
1303 }
1304
1305 /**
1306 * Executes a reliable write transaction for a given remote device.
1307 *
1308 * <p>This function will commit all queued up characteristic write
1309 * operations for a given remote device.
1310 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001311 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001312 * invoked to indicate whether the transaction has been executed correctly.
1313 *
1314 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1315 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001316 * @return true, if the request to execute the transaction has been sent
1317 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001318 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001319 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001320 if (mService == null || mClientIf == 0) return false;
1321
Jack Hea355e5e2017-08-22 16:06:54 -07001322 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001323 if (mDeviceBusy) return false;
1324 mDeviceBusy = true;
1325 }
1326
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001327 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001328 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001329 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001330 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001331 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001332 return false;
1333 }
1334
1335 return true;
1336 }
1337
1338 /**
1339 * Cancels a reliable write transaction for a given device.
1340 *
1341 * <p>Calling this function will discard all queued characteristic write
1342 * operations for a given remote device.
1343 *
1344 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001345 */
John Du48f8b5d2013-08-19 12:20:37 -07001346 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001347 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001348 if (mService == null || mClientIf == 0) return;
1349
1350 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001351 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001352 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001353 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001354 }
1355 }
1356
1357 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001358 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001359 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001360 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001361 public void abortReliableWrite(BluetoothDevice mDevice) {
1362 abortReliableWrite();
1363 }
1364
1365 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001366 * Enable or disable notifications/indications for a given characteristic.
1367 *
1368 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001369 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001370 * triggered if the remote device indicates that the given characteristic
1371 * has changed.
1372 *
1373 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1374 *
1375 * @param characteristic The characteristic for which to enable notifications
1376 * @param enable Set to true to enable notifications/indications
1377 * @return true, if the requested notification status was set successfully
1378 */
1379 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001380 boolean enable) {
1381 if (DBG) {
1382 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1383 + " enable: " + enable);
1384 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001385 if (mService == null || mClientIf == 0) return false;
1386
1387 BluetoothGattService service = characteristic.getService();
1388 if (service == null) return false;
1389
1390 BluetoothDevice device = service.getDevice();
1391 if (device == null) return false;
1392
1393 try {
1394 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001395 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001396 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001397 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001398 return false;
1399 }
1400
1401 return true;
1402 }
1403
1404 /**
1405 * Clears the internal cache and forces a refresh of the services from the
1406 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001407 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001408 * @hide
1409 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001410 public boolean refresh() {
1411 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001412 if (mService == null || mClientIf == 0) return false;
1413
1414 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001415 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001416 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001417 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001418 return false;
1419 }
1420
1421 return true;
1422 }
1423
1424 /**
1425 * Read the RSSI for a connected remote device.
1426 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001427 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001428 * invoked when the RSSI value has been read.
1429 *
1430 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1431 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001432 * @return true, if the RSSI value has been requested successfully
1433 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001434 public boolean readRemoteRssi() {
1435 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001436 if (mService == null || mClientIf == 0) return false;
1437
1438 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001439 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001440 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001441 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001442 return false;
1443 }
1444
1445 return true;
1446 }
1447
1448 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001449 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001450 *
1451 * <p>When performing a write request operation (write without response),
1452 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001453 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001454 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001455 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001456 * whether this operation was successful.
1457 *
1458 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1459 *
1460 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001461 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001462 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001463 if (DBG) {
1464 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1465 + " mtu: " + mtu);
1466 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001467 if (mService == null || mClientIf == 0) return false;
1468
1469 try {
1470 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1471 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001472 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001473 return false;
1474 }
1475
1476 return true;
1477 }
1478
1479 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001480 * Request a connection parameter update.
1481 *
1482 * <p>This function will send a connection parameter update request to the
1483 * remote device.
1484 *
Jack Hea355e5e2017-08-22 16:06:54 -07001485 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1486 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1487 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1488 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001489 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001490 public boolean requestConnectionPriority(int connectionPriority) {
Jack He2992cd02017-08-22 21:21:23 -07001491 if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1492 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001493 throw new IllegalArgumentException("connectionPriority not within valid range");
1494 }
1495
Andre Eisenbach4072da02014-08-19 17:58:55 -07001496 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001497 if (mService == null || mClientIf == 0) return false;
1498
1499 try {
1500 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1501 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001502 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001503 return false;
1504 }
1505
1506 return true;
1507 }
1508
1509 /**
Stanley Tng6da1dda2018-01-04 15:42:25 -08001510 * Request an LE connection parameter update.
1511 *
1512 * <p>This function will send an LE connection parameters update request to the remote device.
1513 *
1514 * @return true, if the request is send to the Bluetooth stack.
1515 * @hide
1516 */
Stanley Tng148dd5b2018-03-19 12:28:56 -07001517 public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
1518 int slaveLatency, int supervisionTimeout,
1519 int minConnectionEventLen, int maxConnectionEventLen) {
Stanley Tng6da1dda2018-01-04 15:42:25 -08001520 if (DBG) {
1521 Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
Stanley Tng148dd5b2018-03-19 12:28:56 -07001522 + ")" + (1.25 * minConnectionInterval)
1523 + "msec, max=(" + maxConnectionInterval + ")"
Stanley Tng6da1dda2018-01-04 15:42:25 -08001524 + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
Stanley Tng148dd5b2018-03-19 12:28:56 -07001525 + ", timeout=" + supervisionTimeout + "msec" + ", min_ce="
1526 + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001527 }
1528 if (mService == null || mClientIf == 0) return false;
1529
1530 try {
1531 mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
Stanley Tng148dd5b2018-03-19 12:28:56 -07001532 minConnectionInterval, maxConnectionInterval,
1533 slaveLatency, supervisionTimeout,
1534 minConnectionEventLen, maxConnectionEventLen);
Stanley Tng6da1dda2018-01-04 15:42:25 -08001535 } catch (RemoteException e) {
1536 Log.e(TAG, "", e);
1537 return false;
1538 }
1539
1540 return true;
1541 }
1542
1543 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001544 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1545 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001546 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001547 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001548 */
1549 @Override
1550 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001551 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001552 }
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 List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -07001562 throw new UnsupportedOperationException(
1563 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001564 }
1565
1566 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001567 * Not supported - please use
1568 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1569 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001570 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001571 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001572 */
1573 @Override
1574 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -07001575 throw new UnsupportedOperationException(
1576 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001577 }
1578}