blob: 83a82f7f8353f08afec60159b1769e24d8f0138e [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,
314 brokenRef.getUuid(), brokenRef.getInstanceId(),
315 brokenRef.getType());
316 if (includedService != null) {
317 fixedService.addIncludedService(includedService);
318 } else {
319 Log.e(TAG, "Broken GATT database: can't find included service.");
320 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700321 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700322 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800323
Jack Hea355e5e2017-08-22 16:06:54 -0700324 runOrQueueCallback(new Runnable() {
325 @Override
326 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700327 final BluetoothGattCallback callback = mCallback;
328 if (callback != null) {
329 callback.onServicesDiscovered(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700330 }
331 }
332 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800333 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700334
Jack Hea355e5e2017-08-22 16:06:54 -0700335 /**
336 * Remote characteristic has been read.
337 * Updates the internal value.
338 * @hide
339 */
340 @Override
341 public void onCharacteristicRead(String address, int status, int handle,
342 byte[] value) {
343 if (VDBG) {
344 Log.d(TAG, "onCharacteristicRead() - Device=" + address
345 + " handle=" + handle + " Status=" + status);
346 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700347
Jack Hea355e5e2017-08-22 16:06:54 -0700348 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800349 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800350 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800351
Jack Hea355e5e2017-08-22 16:06:54 -0700352 synchronized (mDeviceBusy) {
353 mDeviceBusy = false;
354 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800355
Jack Hea355e5e2017-08-22 16:06:54 -0700356 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
357 || status == GATT_INSUFFICIENT_ENCRYPTION)
358 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
359 try {
Jack He2992cd02017-08-22 21:21:23 -0700360 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
361 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700362 mService.readCharacteristic(mClientIf, address, handle, authReq);
363 mAuthRetryState++;
364 return;
365 } catch (RemoteException e) {
366 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700367 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700368 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800369
Jack Hea355e5e2017-08-22 16:06:54 -0700370 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800371
Jack Hea355e5e2017-08-22 16:06:54 -0700372 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
373 handle);
374 if (characteristic == null) {
375 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700376 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800377 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800378
Jack Hea355e5e2017-08-22 16:06:54 -0700379 runOrQueueCallback(new Runnable() {
380 @Override
381 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700382 final BluetoothGattCallback callback = mCallback;
383 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700384 if (status == 0) characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700385 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700386 status);
387 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700388 }
Jack Hea355e5e2017-08-22 16:06:54 -0700389 });
390 }
391
392 /**
393 * Characteristic has been written to the remote device.
394 * Let the app know how we did...
395 * @hide
396 */
397 @Override
398 public void onCharacteristicWrite(String address, int status, int handle) {
399 if (VDBG) {
400 Log.d(TAG, "onCharacteristicWrite() - Device=" + address
401 + " handle=" + handle + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700402 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800403
Jack Hea355e5e2017-08-22 16:06:54 -0700404 if (!address.equals(mDevice.getAddress())) {
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700405 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800406 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800407
Jack Hea355e5e2017-08-22 16:06:54 -0700408 synchronized (mDeviceBusy) {
409 mDeviceBusy = false;
410 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800411
Jack Hea355e5e2017-08-22 16:06:54 -0700412 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
413 handle);
414 if (characteristic == null) return;
415
416 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
417 || status == GATT_INSUFFICIENT_ENCRYPTION)
418 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
419 try {
Jack He2992cd02017-08-22 21:21:23 -0700420 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
421 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700422 mService.writeCharacteristic(mClientIf, address, handle,
423 characteristic.getWriteType(), authReq,
424 characteristic.getValue());
425 mAuthRetryState++;
426 return;
427 } catch (RemoteException e) {
428 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700429 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700430 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800431
Jack Hea355e5e2017-08-22 16:06:54 -0700432 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
433
434 runOrQueueCallback(new Runnable() {
435 @Override
436 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700437 final BluetoothGattCallback callback = mCallback;
438 if (callback != null) {
439 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -0700440 status);
441 }
442 }
443 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800444 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700445
Jack Hea355e5e2017-08-22 16:06:54 -0700446 /**
447 * Remote characteristic has been updated.
448 * Updates the internal value.
449 * @hide
450 */
451 @Override
452 public void onNotify(String address, int handle, byte[] value) {
453 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
454
455 if (!address.equals(mDevice.getAddress())) {
456 return;
457 }
458
459 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
460 handle);
461 if (characteristic == null) return;
462
Jack Hea355e5e2017-08-22 16:06:54 -0700463 runOrQueueCallback(new Runnable() {
464 @Override
465 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700466 final BluetoothGattCallback callback = mCallback;
467 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700468 characteristic.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700469 callback.onCharacteristicChanged(BluetoothGatt.this,
Jack Hea355e5e2017-08-22 16:06:54 -0700470 characteristic);
471 }
472 }
473 });
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700474 }
475
Jack Hea355e5e2017-08-22 16:06:54 -0700476 /**
477 * Descriptor has been read.
478 * @hide
479 */
480 @Override
481 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
482 if (VDBG) {
483 Log.d(TAG,
484 "onDescriptorRead() - Device=" + address + " handle=" + handle);
485 }
486
487 if (!address.equals(mDevice.getAddress())) {
488 return;
489 }
490
491 synchronized (mDeviceBusy) {
492 mDeviceBusy = false;
493 }
494
495 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
496 if (descriptor == null) return;
497
Jack Hea355e5e2017-08-22 16:06:54 -0700498
499 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
500 || status == GATT_INSUFFICIENT_ENCRYPTION)
501 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
502 try {
Jack He2992cd02017-08-22 21:21:23 -0700503 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
504 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700505 mService.readDescriptor(mClientIf, address, handle, authReq);
506 mAuthRetryState++;
507 return;
508 } catch (RemoteException e) {
509 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700510 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700511 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800512
Jack Hea355e5e2017-08-22 16:06:54 -0700513 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
514
515 runOrQueueCallback(new Runnable() {
516 @Override
517 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700518 final BluetoothGattCallback callback = mCallback;
519 if (callback != null) {
Jakub Pawlowskieb6b3da2017-09-12 14:48:30 -0700520 if (status == 0) descriptor.setValue(value);
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700521 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700522 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700523 }
Jack Hea355e5e2017-08-22 16:06:54 -0700524 });
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700525 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700526
Jack Hea355e5e2017-08-22 16:06:54 -0700527 /**
528 * Descriptor write operation complete.
529 * @hide
530 */
531 @Override
532 public void onDescriptorWrite(String address, int status, int handle) {
533 if (VDBG) {
534 Log.d(TAG,
535 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
536 }
537
538 if (!address.equals(mDevice.getAddress())) {
539 return;
540 }
541
542 synchronized (mDeviceBusy) {
543 mDeviceBusy = false;
544 }
545
546 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
547 if (descriptor == null) return;
548
549 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
550 || status == GATT_INSUFFICIENT_ENCRYPTION)
551 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
552 try {
Jack He2992cd02017-08-22 21:21:23 -0700553 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
554 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700555 mService.writeDescriptor(mClientIf, address, handle,
556 authReq, descriptor.getValue());
557 mAuthRetryState++;
558 return;
559 } catch (RemoteException e) {
560 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700561 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700562 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700563
Jack Hea355e5e2017-08-22 16:06:54 -0700564 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
565
566 runOrQueueCallback(new Runnable() {
567 @Override
568 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700569 final BluetoothGattCallback callback = mCallback;
570 if (callback != null) {
571 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700572 }
573 }
574 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700575 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700576
Jack Hea355e5e2017-08-22 16:06:54 -0700577 /**
578 * Prepared write transaction completed (or aborted)
579 * @hide
580 */
581 @Override
582 public void onExecuteWrite(String address, int status) {
583 if (VDBG) {
584 Log.d(TAG, "onExecuteWrite() - Device=" + address
585 + " status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700586 }
Jack Hea355e5e2017-08-22 16:06:54 -0700587 if (!address.equals(mDevice.getAddress())) {
588 return;
589 }
590
591 synchronized (mDeviceBusy) {
592 mDeviceBusy = false;
593 }
594
595 runOrQueueCallback(new Runnable() {
596 @Override
597 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700598 final BluetoothGattCallback callback = mCallback;
599 if (callback != null) {
600 callback.onReliableWriteCompleted(BluetoothGatt.this, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700601 }
602 }
603 });
604 }
605
606 /**
607 * Remote device RSSI has been read
608 * @hide
609 */
610 @Override
611 public void onReadRemoteRssi(String address, int rssi, int status) {
612 if (VDBG) {
Jack He2992cd02017-08-22 21:21:23 -0700613 Log.d(TAG, "onReadRemoteRssi() - Device=" + address
614 + " rssi=" + rssi + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700615 }
616 if (!address.equals(mDevice.getAddress())) {
617 return;
618 }
619 runOrQueueCallback(new Runnable() {
620 @Override
621 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700622 final BluetoothGattCallback callback = mCallback;
623 if (callback != null) {
624 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700625 }
626 }
627 });
628 }
629
630 /**
631 * Callback invoked when the MTU for a given connection changes
632 * @hide
633 */
634 @Override
635 public void onConfigureMTU(String address, int mtu, int status) {
636 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700637 Log.d(TAG, "onConfigureMTU() - Device=" + address
638 + " mtu=" + mtu + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700639 }
640 if (!address.equals(mDevice.getAddress())) {
641 return;
642 }
643
644 runOrQueueCallback(new Runnable() {
645 @Override
646 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700647 final BluetoothGattCallback callback = mCallback;
648 if (callback != null) {
649 callback.onMtuChanged(BluetoothGatt.this, mtu, status);
Jack Hea355e5e2017-08-22 16:06:54 -0700650 }
651 }
652 });
653 }
654
655 /**
656 * Callback invoked when the given connection is updated
657 * @hide
658 */
659 @Override
660 public void onConnectionUpdated(String address, int interval, int latency,
661 int timeout, int status) {
662 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700663 Log.d(TAG, "onConnectionUpdated() - Device=" + address
664 + " interval=" + interval + " latency=" + latency
665 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700666 }
667 if (!address.equals(mDevice.getAddress())) {
668 return;
669 }
670
671 runOrQueueCallback(new Runnable() {
672 @Override
673 public void run() {
Jakub Pawlowski3eb569f2017-09-13 09:33:34 -0700674 final BluetoothGattCallback callback = mCallback;
675 if (callback != null) {
676 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
Jack Hea355e5e2017-08-22 16:06:54 -0700677 timeout, status);
678 }
679 }
680 });
681 }
682 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800683
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700684 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700685 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800686 mService = iGatt;
687 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700688 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800689 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700690 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800691 mServices = new ArrayList<BluetoothGattService>();
692
Matthew Xieddf7e472013-03-01 18:41:02 -0800693 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700694 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800695 }
696
697 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700698 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700699 *
700 * Application should call this method as early as possible after it is done with
701 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700703 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800704 if (DBG) Log.d(TAG, "close()");
705
706 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700707 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700708 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800709 }
710
711 /**
712 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700713 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800714 * @hide
715 */
716 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
Jack Hea355e5e2017-08-22 16:06:54 -0700717 int instanceId, int type) {
718 for (BluetoothGattService svc : mServices) {
Jack He2992cd02017-08-22 21:21:23 -0700719 if (svc.getDevice().equals(device)
720 && svc.getType() == type
721 && svc.getInstanceId() == instanceId
722 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800723 return svc;
724 }
725 }
726 return null;
727 }
728
729
730 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700731 * Returns a characteristic with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700732 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700733 * @hide
734 */
Jack Hea355e5e2017-08-22 16:06:54 -0700735 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
736 int instanceId) {
737 for (BluetoothGattService svc : mServices) {
738 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
739 if (charac.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700740 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700741 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700742 }
743 }
744 return null;
745 }
746
747 /**
748 * Returns a descriptor with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700749 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700750 * @hide
751 */
752 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700753 for (BluetoothGattService svc : mServices) {
754 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
755 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
756 if (desc.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700757 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700758 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700759 }
760 }
761 }
762 return null;
763 }
764
765 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700766 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
767 * immediately if no Handler was provided.
768 */
769 private void runOrQueueCallback(final Runnable cb) {
770 if (mHandler == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700771 try {
772 cb.run();
773 } catch (Exception ex) {
774 Log.w(TAG, "Unhandled exception in callback", ex);
775 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700776 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700777 mHandler.post(cb);
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700778 }
779 }
780
781 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800782 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800783 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700784 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800785 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800786 *
787 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
788 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800789 * @param callback GATT callback handler that will receive asynchronous callbacks.
Jack Hea355e5e2017-08-22 16:06:54 -0700790 * @return If true, the callback will be called to notify success or failure, false on immediate
791 * error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800792 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700793 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800794 if (DBG) Log.d(TAG, "registerApp()");
795 if (mService == null) return false;
796
797 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700798 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800799 UUID uuid = UUID.randomUUID();
800 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
801
802 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700803 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800804 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700805 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 return false;
807 }
808
809 return true;
810 }
811
812 /**
813 * Unregister the current application and callbacks.
814 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800815 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800816 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
817 if (mService == null || mClientIf == 0) return;
818
819 try {
820 mCallback = null;
821 mService.unregisterClient(mClientIf);
822 mClientIf = 0;
823 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700824 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 }
826 }
827
828 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800829 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800830 *
831 * <p>The connection may not be established right away, but will be
832 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700833 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800834 * invoked when the connection state changes as a result of this function.
835 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700836 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800837 * the remote device, or rather passively scan and finalize the connection
838 * when the remote device is in range/available. Generally, the first ever
839 * connection to a device should be direct (autoConnect set to false) and
840 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800841 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800842 *
843 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
844 *
845 * @param device Remote device to connect to
Jack Hea355e5e2017-08-22 16:06:54 -0700846 * @param autoConnect Whether to directly connect to the remote device (false) or to
847 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800848 * @return true, if the connection attempt was initiated successfully
849 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700850 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700851 Handler handler) {
852 if (DBG) {
853 Log.d(TAG,
854 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
855 }
856 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800857 if (mConnState != CONN_STATE_IDLE) {
858 throw new IllegalStateException("Not idle");
859 }
860 mConnState = CONN_STATE_CONNECTING;
861 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700862
863 mAutoConnect = autoConnect;
864
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700865 if (!registerApp(callback, handler)) {
Jack Hea355e5e2017-08-22 16:06:54 -0700866 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800867 mConnState = CONN_STATE_IDLE;
868 }
869 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800870 return false;
871 }
872
Sungki Kimd35167a2016-05-19 10:18:07 -0700873 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800874 return true;
875 }
876
877 /**
878 * Disconnects an established connection, or cancels a connection attempt
879 * currently in progress.
880 *
881 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800882 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800883 public void disconnect() {
884 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800885 if (mService == null || mClientIf == 0) return;
886
887 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800888 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800889 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700890 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800891 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700892 }
893
894 /**
895 * Connect back to remote device.
896 *
897 * <p>This method is used to re-connect to a remote device after the
898 * connection has been dropped. If the device is not in range, the
899 * re-connection will be triggered once the device is back in range.
900 *
901 * @return true, if the connection attempt was initiated successfully
902 */
903 public boolean connect() {
904 try {
Jack He13f52c82017-07-05 14:55:35 -0700905 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
906 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700907 return true;
908 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700909 Log.e(TAG, "", e);
Matthew Xie33ec9842013-04-03 00:29:27 -0700910 return false;
911 }
912 }
913
914 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800915 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskif752ace2017-03-22 22:44:09 -0700916 * recommendation, whether the PHY change will happen depends on other applications peferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800917 * local and remote controller capabilities. Controller can override these settings.
918 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700919 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800920 * if no PHY change happens. It is also triggered when remote device updates the PHY.
921 *
Jack Hea355e5e2017-08-22 16:06:54 -0700922 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
923 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
924 * BluetoothDevice#PHY_LE_CODED_MASK}.
925 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
926 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
927 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800928 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700929 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
930 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800931 */
932 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
933 try {
934 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700935 phyOptions);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800936 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700937 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800938 }
939 }
940
941 /**
942 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700943 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800944 */
945 public void readPhy() {
946 try {
947 mService.clientReadPhy(mClientIf, mDevice.getAddress());
948 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700949 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800950 }
951 }
952
953 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700954 * Return the remote bluetooth device this GATT client targets to
955 *
956 * @return remote bluetooth device
957 */
958 public BluetoothDevice getDevice() {
959 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800960 }
961
962 /**
963 * Discovers services offered by a remote device as well as their
964 * characteristics and descriptors.
965 *
966 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700967 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800968 * triggered. If the discovery was successful, the remote services can be
969 * retrieved using the {@link #getServices} function.
970 *
971 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
972 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800973 * @return true, if the remote service discovery has been started
974 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800975 public boolean discoverServices() {
976 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800977 if (mService == null || mClientIf == 0) return false;
978
979 mServices.clear();
980
981 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800982 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800983 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700984 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800985 return false;
986 }
987
988 return true;
989 }
990
991 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -0700992 * Discovers a service by UUID. This is exposed only for passing PTS tests.
993 * It should never be used by real applications. The service is not searched
994 * for characteristics and descriptors, or returned in any callback.
995 *
996 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
997 *
998 * @return true, if the remote service discovery has been started
999 * @hide
1000 */
1001 public boolean discoverServiceByUuid(UUID uuid) {
1002 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
1003 if (mService == null || mClientIf == 0) return false;
1004
1005 mServices.clear();
1006
1007 try {
1008 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
1009 } catch (RemoteException e) {
1010 Log.e(TAG, "", e);
1011 return false;
1012 }
1013 return true;
1014 }
1015
1016 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001017 * Returns a list of GATT services offered by the remote device.
1018 *
1019 * <p>This function requires that service discovery has been completed
1020 * for the given device.
1021 *
1022 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1023 *
Jack Hea355e5e2017-08-22 16:06:54 -07001024 * @return List of services on the remote device. Returns an empty list if service discovery has
1025 * not yet been performed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001026 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001027 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001028 List<BluetoothGattService> result =
1029 new ArrayList<BluetoothGattService>();
1030
1031 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001032 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001033 result.add(service);
1034 }
1035 }
1036
1037 return result;
1038 }
1039
1040 /**
1041 * Returns a {@link BluetoothGattService}, if the requested UUID is
1042 * supported by the remote device.
1043 *
1044 * <p>This function requires that service discovery has been completed
1045 * for the given device.
1046 *
1047 * <p>If multiple instances of the same service (as identified by UUID)
1048 * exist, the first instance of the service is returned.
1049 *
1050 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1051 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001052 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -07001053 * @return BluetoothGattService if supported, or null if the requested service is not offered by
1054 * the remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001055 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001056 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001057 for (BluetoothGattService service : mServices) {
Jack He2992cd02017-08-22 21:21:23 -07001058 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001059 return service;
1060 }
1061 }
1062
1063 return null;
1064 }
1065
1066 /**
1067 * Reads the requested characteristic from the associated remote device.
1068 *
1069 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001070 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001071 * callback.
1072 *
1073 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1074 *
1075 * @param characteristic Characteristic to read from the remote device
1076 * @return true, if the read operation was initiated successfully
1077 */
1078 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
Jack He2992cd02017-08-22 21:21:23 -07001079 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001080 return false;
1081 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001082
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001083 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001084 if (mService == null || mClientIf == 0) return false;
1085
1086 BluetoothGattService service = characteristic.getService();
1087 if (service == null) return false;
1088
1089 BluetoothDevice device = service.getDevice();
1090 if (device == null) return false;
1091
Jack Hea355e5e2017-08-22 16:06:54 -07001092 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001093 if (mDeviceBusy) return false;
1094 mDeviceBusy = true;
1095 }
1096
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001097 try {
1098 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001099 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001100 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001101 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001102 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001110 * Reads the characteristic using its UUID from the associated remote device.
1111 *
1112 * <p>This is an asynchronous operation. The result of the read operation
1113 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1114 * callback.
1115 *
1116 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1117 *
1118 * @param uuid UUID of characteristic to read from the remote device
1119 * @return true, if the read operation was initiated successfully
1120 * @hide
1121 */
1122 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1123 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1124 if (mService == null || mClientIf == 0) return false;
1125
Jack Hea355e5e2017-08-22 16:06:54 -07001126 synchronized (mDeviceBusy) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001127 if (mDeviceBusy) return false;
1128 mDeviceBusy = true;
1129 }
1130
1131 try {
1132 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001133 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001134 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001135 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001136 mDeviceBusy = false;
1137 return false;
1138 }
1139
1140 return true;
1141 }
1142
1143
1144 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001145 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001146 *
1147 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001148 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001149 * reporting the result of the operation.
1150 *
1151 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1152 *
1153 * @param characteristic Characteristic to write on the remote device
1154 * @return true, if the write operation was initiated successfully
1155 */
1156 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1157 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack He2992cd02017-08-22 21:21:23 -07001158 && (characteristic.getProperties()
1159 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001160 return false;
1161 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001162
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001163 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001164 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001165
1166 BluetoothGattService service = characteristic.getService();
1167 if (service == null) return false;
1168
1169 BluetoothDevice device = service.getDevice();
1170 if (device == null) return false;
1171
Jack Hea355e5e2017-08-22 16:06:54 -07001172 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001173 if (mDeviceBusy) return false;
1174 mDeviceBusy = true;
1175 }
1176
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001177 try {
1178 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001179 characteristic.getInstanceId(), characteristic.getWriteType(),
1180 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001181 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001182 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001183 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001184 return false;
1185 }
1186
1187 return true;
1188 }
1189
1190 /**
1191 * Reads the value for a given descriptor from the associated remote device.
1192 *
1193 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001194 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001195 * triggered, signaling the result of the operation.
1196 *
1197 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1198 *
1199 * @param descriptor Descriptor value to read from the remote device
1200 * @return true, if the read operation was initiated successfully
1201 */
1202 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001203 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001204 if (mService == null || mClientIf == 0) return false;
1205
1206 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1207 if (characteristic == null) return false;
1208
1209 BluetoothGattService service = characteristic.getService();
1210 if (service == null) return false;
1211
1212 BluetoothDevice device = service.getDevice();
1213 if (device == null) return false;
1214
Jack Hea355e5e2017-08-22 16:06:54 -07001215 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001216 if (mDeviceBusy) return false;
1217 mDeviceBusy = true;
1218 }
1219
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001220 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001221 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001222 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001223 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001224 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001225 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001226 return false;
1227 }
1228
1229 return true;
1230 }
1231
1232 /**
1233 * Write the value of a given descriptor to the associated remote device.
1234 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001235 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001236 * triggered to report the result of the write operation.
1237 *
1238 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1239 *
1240 * @param descriptor Descriptor to write to the associated remote device
1241 * @return true, if the write operation was initiated successfully
1242 */
1243 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001244 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001245 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001246
1247 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1248 if (characteristic == null) return false;
1249
1250 BluetoothGattService service = characteristic.getService();
1251 if (service == null) return false;
1252
1253 BluetoothDevice device = service.getDevice();
1254 if (device == null) return false;
1255
Jack Hea355e5e2017-08-22 16:06:54 -07001256 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001257 if (mDeviceBusy) return false;
1258 mDeviceBusy = true;
1259 }
1260
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001261 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001262 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001263 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001264 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001265 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001266 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001267 return false;
1268 }
1269
1270 return true;
1271 }
1272
1273 /**
1274 * Initiates a reliable write transaction for a given remote device.
1275 *
1276 * <p>Once a reliable write transaction has been initiated, all calls
1277 * to {@link #writeCharacteristic} are sent to the remote device for
1278 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001279 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001280 * in response to every {@link #writeCharacteristic} call and is responsible
1281 * for verifying if the value has been transmitted accurately.
1282 *
1283 * <p>After all characteristics have been queued up and verified,
1284 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1285 * was not written correctly, calling {@link #abortReliableWrite} will
1286 * cancel the current transaction without commiting any values on the
1287 * remote device.
1288 *
1289 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1290 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001291 * @return true, if the reliable write transaction has been initiated
1292 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001293 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001294 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001295 if (mService == null || mClientIf == 0) return false;
1296
1297 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001298 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001299 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001300 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001301 return false;
1302 }
1303
1304 return true;
1305 }
1306
1307 /**
1308 * Executes a reliable write transaction for a given remote device.
1309 *
1310 * <p>This function will commit all queued up characteristic write
1311 * operations for a given remote device.
1312 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001313 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001314 * invoked to indicate whether the transaction has been executed correctly.
1315 *
1316 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1317 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001318 * @return true, if the request to execute the transaction has been sent
1319 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001320 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001321 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001322 if (mService == null || mClientIf == 0) return false;
1323
Jack Hea355e5e2017-08-22 16:06:54 -07001324 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001325 if (mDeviceBusy) return false;
1326 mDeviceBusy = true;
1327 }
1328
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001329 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001330 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001331 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001332 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001333 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001334 return false;
1335 }
1336
1337 return true;
1338 }
1339
1340 /**
1341 * Cancels a reliable write transaction for a given device.
1342 *
1343 * <p>Calling this function will discard all queued characteristic write
1344 * operations for a given remote device.
1345 *
1346 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001347 */
John Du48f8b5d2013-08-19 12:20:37 -07001348 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001349 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001350 if (mService == null || mClientIf == 0) return;
1351
1352 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001353 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001354 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001355 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001356 }
1357 }
1358
1359 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001360 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001361 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001362 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001363 public void abortReliableWrite(BluetoothDevice mDevice) {
1364 abortReliableWrite();
1365 }
1366
1367 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001368 * Enable or disable notifications/indications for a given characteristic.
1369 *
1370 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001371 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001372 * triggered if the remote device indicates that the given characteristic
1373 * has changed.
1374 *
1375 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1376 *
1377 * @param characteristic The characteristic for which to enable notifications
1378 * @param enable Set to true to enable notifications/indications
1379 * @return true, if the requested notification status was set successfully
1380 */
1381 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001382 boolean enable) {
1383 if (DBG) {
1384 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1385 + " enable: " + enable);
1386 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001387 if (mService == null || mClientIf == 0) return false;
1388
1389 BluetoothGattService service = characteristic.getService();
1390 if (service == null) return false;
1391
1392 BluetoothDevice device = service.getDevice();
1393 if (device == null) return false;
1394
1395 try {
1396 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001397 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001398 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001399 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001400 return false;
1401 }
1402
1403 return true;
1404 }
1405
1406 /**
1407 * Clears the internal cache and forces a refresh of the services from the
1408 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001409 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001410 * @hide
1411 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001412 public boolean refresh() {
1413 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001414 if (mService == null || mClientIf == 0) return false;
1415
1416 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001417 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001418 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001419 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001420 return false;
1421 }
1422
1423 return true;
1424 }
1425
1426 /**
1427 * Read the RSSI for a connected remote device.
1428 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001429 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001430 * invoked when the RSSI value has been read.
1431 *
1432 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1433 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001434 * @return true, if the RSSI value has been requested successfully
1435 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001436 public boolean readRemoteRssi() {
1437 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001438 if (mService == null || mClientIf == 0) return false;
1439
1440 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001441 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001442 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001443 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001444 return false;
1445 }
1446
1447 return true;
1448 }
1449
1450 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001451 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001452 *
1453 * <p>When performing a write request operation (write without response),
1454 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001455 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001456 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001457 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001458 * whether this operation was successful.
1459 *
1460 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1461 *
1462 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001463 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001464 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001465 if (DBG) {
1466 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1467 + " mtu: " + mtu);
1468 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001469 if (mService == null || mClientIf == 0) return false;
1470
1471 try {
1472 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1473 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001474 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001475 return false;
1476 }
1477
1478 return true;
1479 }
1480
1481 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001482 * Request a connection parameter update.
1483 *
1484 * <p>This function will send a connection parameter update request to the
1485 * remote device.
1486 *
Jack Hea355e5e2017-08-22 16:06:54 -07001487 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1488 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1489 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1490 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001491 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001492 public boolean requestConnectionPriority(int connectionPriority) {
Jack He2992cd02017-08-22 21:21:23 -07001493 if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1494 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001495 throw new IllegalArgumentException("connectionPriority not within valid range");
1496 }
1497
Andre Eisenbach4072da02014-08-19 17:58:55 -07001498 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001499 if (mService == null || mClientIf == 0) return false;
1500
1501 try {
1502 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1503 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001504 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001505 return false;
1506 }
1507
1508 return true;
1509 }
1510
1511 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001512 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1513 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001514 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001515 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001516 */
1517 @Override
1518 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001519 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001520 }
1521
1522 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001523 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1524 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001525 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001526 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001527 */
1528 @Override
1529 public List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -07001530 throw new UnsupportedOperationException(
1531 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001532 }
1533
1534 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001535 * Not supported - please use
1536 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1537 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001538 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001539 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001540 */
1541 @Override
1542 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -07001543 throw new UnsupportedOperationException(
1544 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001545 }
1546}