blob: 8a3650c86989cca256ad54c476aed42c0884eecf [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 Pawlowskid64bb882017-03-22 11:22:18 -070045 private 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() {
167 if (mCallback != null) {
168 mCallback.onConnectionStateChange(BluetoothGatt.this,
169 GATT_FAILURE,
170 BluetoothProfile.STATE_DISCONNECTED);
171 }
172 }
173 });
174
175 synchronized (mStateLock) {
176 mConnState = CONN_STATE_IDLE;
177 }
178 return;
179 }
180 try {
181 mService.clientConnect(mClientIf, mDevice.getAddress(),
182 !mAutoConnect, mTransport, mOpportunistic,
183 mPhy); // autoConnect is inverse of "isDirect"
184 } catch (RemoteException e) {
185 Log.e(TAG, "", e);
186 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800187 }
Jack Hea355e5e2017-08-22 16:06:54 -0700188
189 /**
190 * Phy update callback
191 * @hide
192 */
193 @Override
194 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
195 if (DBG) {
196 Log.d(TAG, "onPhyUpdate() - status=" + status
197 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
198 }
199 if (!address.equals(mDevice.getAddress())) {
200 return;
201 }
202
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700203 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700204 @Override
205 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700206 if (mCallback != null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700207 mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
208 }
209 }
210 });
211 }
212
213 /**
214 * Phy read callback
215 * @hide
216 */
217 @Override
218 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
219 if (DBG) {
220 Log.d(TAG, "onPhyRead() - status=" + status
221 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
222 }
223 if (!address.equals(mDevice.getAddress())) {
224 return;
225 }
226
227 runOrQueueCallback(new Runnable() {
228 @Override
229 public void run() {
230 if (mCallback != null) {
231 mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
232 }
233 }
234 });
235 }
236
237 /**
238 * Client connection state changed
239 * @hide
240 */
241 @Override
242 public void onClientConnectionState(int status, int clientIf,
243 boolean connected, String address) {
244 if (DBG) {
245 Log.d(TAG, "onClientConnectionState() - status=" + status
246 + " clientIf=" + clientIf + " device=" + address);
247 }
248 if (!address.equals(mDevice.getAddress())) {
249 return;
250 }
251 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
252 BluetoothProfile.STATE_DISCONNECTED;
253
254 runOrQueueCallback(new Runnable() {
255 @Override
256 public void run() {
257 if (mCallback != null) {
258 mCallback.onConnectionStateChange(BluetoothGatt.this, status,
259 profileState);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700260 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700261 }
262 });
263
Jack Hea355e5e2017-08-22 16:06:54 -0700264 synchronized (mStateLock) {
265 if (connected) {
266 mConnState = CONN_STATE_CONNECTED;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800267 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700268 mConnState = CONN_STATE_IDLE;
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800269 }
270 }
Jack Hea355e5e2017-08-22 16:06:54 -0700271
272 synchronized (mDeviceBusy) {
273 mDeviceBusy = false;
274 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800275 }
276
Jack Hea355e5e2017-08-22 16:06:54 -0700277 /**
278 * Remote search has been completed.
279 * The internal object structure should now reflect the state
280 * of the remote device database. Let the application know that
281 * we are done at this point.
282 * @hide
283 */
284 @Override
285 public void onSearchComplete(String address, List<BluetoothGattService> services,
286 int status) {
287 if (DBG) {
288 Log.d(TAG,
289 "onSearchComplete() = Device=" + address + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700290 }
Jack Hea355e5e2017-08-22 16:06:54 -0700291 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800292 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800293 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800294
Jack Hea355e5e2017-08-22 16:06:54 -0700295 for (BluetoothGattService s : services) {
296 //services we receive don't have device set properly.
297 s.setDevice(mDevice);
298 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800299
Jack Hea355e5e2017-08-22 16:06:54 -0700300 mServices.addAll(services);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800301
Jack Hea355e5e2017-08-22 16:06:54 -0700302 // Fix references to included services, as they doesn't point to right objects.
303 for (BluetoothGattService fixedService : mServices) {
304 ArrayList<BluetoothGattService> includedServices =
305 new ArrayList(fixedService.getIncludedServices());
306 fixedService.getIncludedServices().clear();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800307
Jack Hea355e5e2017-08-22 16:06:54 -0700308 for (BluetoothGattService brokenRef : includedServices) {
309 BluetoothGattService includedService = getService(mDevice,
310 brokenRef.getUuid(), brokenRef.getInstanceId(),
311 brokenRef.getType());
312 if (includedService != null) {
313 fixedService.addIncludedService(includedService);
314 } else {
315 Log.e(TAG, "Broken GATT database: can't find included service.");
316 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700317 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700318 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800319
Jack Hea355e5e2017-08-22 16:06:54 -0700320 runOrQueueCallback(new Runnable() {
321 @Override
322 public void run() {
323 if (mCallback != null) {
324 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
325 }
326 }
327 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800328 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700329
Jack Hea355e5e2017-08-22 16:06:54 -0700330 /**
331 * Remote characteristic has been read.
332 * Updates the internal value.
333 * @hide
334 */
335 @Override
336 public void onCharacteristicRead(String address, int status, int handle,
337 byte[] value) {
338 if (VDBG) {
339 Log.d(TAG, "onCharacteristicRead() - Device=" + address
340 + " handle=" + handle + " Status=" + status);
341 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700342
Jack Hea355e5e2017-08-22 16:06:54 -0700343 if (!address.equals(mDevice.getAddress())) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800344 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800345 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346
Jack Hea355e5e2017-08-22 16:06:54 -0700347 synchronized (mDeviceBusy) {
348 mDeviceBusy = false;
349 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800350
Jack Hea355e5e2017-08-22 16:06:54 -0700351 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
352 || status == GATT_INSUFFICIENT_ENCRYPTION)
353 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
354 try {
355 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
356 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
357 mService.readCharacteristic(mClientIf, address, handle, authReq);
358 mAuthRetryState++;
359 return;
360 } catch (RemoteException e) {
361 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700362 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700363 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800364
Jack Hea355e5e2017-08-22 16:06:54 -0700365 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800366
Jack Hea355e5e2017-08-22 16:06:54 -0700367 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
368 handle);
369 if (characteristic == null) {
370 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700371 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800372 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800373
Jack Hea355e5e2017-08-22 16:06:54 -0700374 if (status == 0) characteristic.setValue(value);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800375
Jack Hea355e5e2017-08-22 16:06:54 -0700376 runOrQueueCallback(new Runnable() {
377 @Override
378 public void run() {
379 if (mCallback != null) {
380 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic,
381 status);
382 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700383 }
Jack Hea355e5e2017-08-22 16:06:54 -0700384 });
385 }
386
387 /**
388 * Characteristic has been written to the remote device.
389 * Let the app know how we did...
390 * @hide
391 */
392 @Override
393 public void onCharacteristicWrite(String address, int status, int handle) {
394 if (VDBG) {
395 Log.d(TAG, "onCharacteristicWrite() - Device=" + address
396 + " handle=" + handle + " Status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700397 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800398
Jack Hea355e5e2017-08-22 16:06:54 -0700399 if (!address.equals(mDevice.getAddress())) {
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700400 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800401 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800402
Jack Hea355e5e2017-08-22 16:06:54 -0700403 synchronized (mDeviceBusy) {
404 mDeviceBusy = false;
405 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800406
Jack Hea355e5e2017-08-22 16:06:54 -0700407 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
408 handle);
409 if (characteristic == null) return;
410
411 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
412 || status == GATT_INSUFFICIENT_ENCRYPTION)
413 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
414 try {
415 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
416 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
417 mService.writeCharacteristic(mClientIf, address, handle,
418 characteristic.getWriteType(), authReq,
419 characteristic.getValue());
420 mAuthRetryState++;
421 return;
422 } catch (RemoteException e) {
423 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700424 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700425 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800426
Jack Hea355e5e2017-08-22 16:06:54 -0700427 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
428
429 runOrQueueCallback(new Runnable() {
430 @Override
431 public void run() {
432 if (mCallback != null) {
433 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
434 status);
435 }
436 }
437 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800438 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700439
Jack Hea355e5e2017-08-22 16:06:54 -0700440 /**
441 * Remote characteristic has been updated.
442 * Updates the internal value.
443 * @hide
444 */
445 @Override
446 public void onNotify(String address, int handle, byte[] value) {
447 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
448
449 if (!address.equals(mDevice.getAddress())) {
450 return;
451 }
452
453 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice,
454 handle);
455 if (characteristic == null) return;
456
457 characteristic.setValue(value);
458
459 runOrQueueCallback(new Runnable() {
460 @Override
461 public void run() {
462 if (mCallback != null) {
463 mCallback.onCharacteristicChanged(BluetoothGatt.this,
464 characteristic);
465 }
466 }
467 });
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700468 }
469
Jack Hea355e5e2017-08-22 16:06:54 -0700470 /**
471 * Descriptor has been read.
472 * @hide
473 */
474 @Override
475 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
476 if (VDBG) {
477 Log.d(TAG,
478 "onDescriptorRead() - Device=" + address + " handle=" + handle);
479 }
480
481 if (!address.equals(mDevice.getAddress())) {
482 return;
483 }
484
485 synchronized (mDeviceBusy) {
486 mDeviceBusy = false;
487 }
488
489 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
490 if (descriptor == null) return;
491
492 if (status == 0) descriptor.setValue(value);
493
494 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
495 || status == GATT_INSUFFICIENT_ENCRYPTION)
496 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
497 try {
498 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
499 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
500 mService.readDescriptor(mClientIf, address, handle, authReq);
501 mAuthRetryState++;
502 return;
503 } catch (RemoteException e) {
504 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700505 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700506 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800507
Jack Hea355e5e2017-08-22 16:06:54 -0700508 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
509
510 runOrQueueCallback(new Runnable() {
511 @Override
512 public void run() {
513 if (mCallback != null) {
514 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
515 }
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700516 }
Jack Hea355e5e2017-08-22 16:06:54 -0700517 });
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700518 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700519
Jack Hea355e5e2017-08-22 16:06:54 -0700520 /**
521 * Descriptor write operation complete.
522 * @hide
523 */
524 @Override
525 public void onDescriptorWrite(String address, int status, int handle) {
526 if (VDBG) {
527 Log.d(TAG,
528 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
529 }
530
531 if (!address.equals(mDevice.getAddress())) {
532 return;
533 }
534
535 synchronized (mDeviceBusy) {
536 mDeviceBusy = false;
537 }
538
539 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
540 if (descriptor == null) return;
541
542 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
543 || status == GATT_INSUFFICIENT_ENCRYPTION)
544 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
545 try {
546 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
547 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
548 mService.writeDescriptor(mClientIf, address, handle,
549 authReq, descriptor.getValue());
550 mAuthRetryState++;
551 return;
552 } catch (RemoteException e) {
553 Log.e(TAG, "", e);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700554 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700555 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700556
Jack Hea355e5e2017-08-22 16:06:54 -0700557 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
558
559 runOrQueueCallback(new Runnable() {
560 @Override
561 public void run() {
562 if (mCallback != null) {
563 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
564 }
565 }
566 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700567 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700568
Jack Hea355e5e2017-08-22 16:06:54 -0700569 /**
570 * Prepared write transaction completed (or aborted)
571 * @hide
572 */
573 @Override
574 public void onExecuteWrite(String address, int status) {
575 if (VDBG) {
576 Log.d(TAG, "onExecuteWrite() - Device=" + address
577 + " status=" + status);
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700578 }
Jack Hea355e5e2017-08-22 16:06:54 -0700579 if (!address.equals(mDevice.getAddress())) {
580 return;
581 }
582
583 synchronized (mDeviceBusy) {
584 mDeviceBusy = false;
585 }
586
587 runOrQueueCallback(new Runnable() {
588 @Override
589 public void run() {
590 if (mCallback != null) {
591 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
592 }
593 }
594 });
595 }
596
597 /**
598 * Remote device RSSI has been read
599 * @hide
600 */
601 @Override
602 public void onReadRemoteRssi(String address, int rssi, int status) {
603 if (VDBG) {
604 Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
605 " rssi=" + rssi + " status=" + status);
606 }
607 if (!address.equals(mDevice.getAddress())) {
608 return;
609 }
610 runOrQueueCallback(new Runnable() {
611 @Override
612 public void run() {
613 if (mCallback != null) {
614 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
615 }
616 }
617 });
618 }
619
620 /**
621 * Callback invoked when the MTU for a given connection changes
622 * @hide
623 */
624 @Override
625 public void onConfigureMTU(String address, int mtu, int status) {
626 if (DBG) {
627 Log.d(TAG, "onConfigureMTU() - Device=" + address +
628 " mtu=" + mtu + " status=" + status);
629 }
630 if (!address.equals(mDevice.getAddress())) {
631 return;
632 }
633
634 runOrQueueCallback(new Runnable() {
635 @Override
636 public void run() {
637 if (mCallback != null) {
638 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
639 }
640 }
641 });
642 }
643
644 /**
645 * Callback invoked when the given connection is updated
646 * @hide
647 */
648 @Override
649 public void onConnectionUpdated(String address, int interval, int latency,
650 int timeout, int status) {
651 if (DBG) {
652 Log.d(TAG, "onConnectionUpdated() - Device=" + address +
653 " interval=" + interval + " latency=" + latency +
654 " timeout=" + timeout + " status=" + status);
655 }
656 if (!address.equals(mDevice.getAddress())) {
657 return;
658 }
659
660 runOrQueueCallback(new Runnable() {
661 @Override
662 public void run() {
663 if (mCallback != null) {
664 mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
665 timeout, status);
666 }
667 }
668 });
669 }
670 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800671
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700672 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700673 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800674 mService = iGatt;
675 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700676 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800677 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700678 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800679 mServices = new ArrayList<BluetoothGattService>();
680
Matthew Xieddf7e472013-03-01 18:41:02 -0800681 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700682 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800683 }
684
685 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700686 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700687 *
688 * Application should call this method as early as possible after it is done with
689 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800690 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700691 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800692 if (DBG) Log.d(TAG, "close()");
693
694 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700695 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700696 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800697 }
698
699 /**
700 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700701 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 * @hide
703 */
704 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
Jack Hea355e5e2017-08-22 16:06:54 -0700705 int instanceId, int type) {
706 for (BluetoothGattService svc : mServices) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800707 if (svc.getDevice().equals(device) &&
Jack Hea355e5e2017-08-22 16:06:54 -0700708 svc.getType() == type &&
709 svc.getInstanceId() == instanceId &&
710 svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800711 return svc;
712 }
713 }
714 return null;
715 }
716
717
718 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700719 * Returns a characteristic with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700720 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700721 * @hide
722 */
Jack Hea355e5e2017-08-22 16:06:54 -0700723 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device,
724 int instanceId) {
725 for (BluetoothGattService svc : mServices) {
726 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
727 if (charac.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700728 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700729 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700730 }
731 }
732 return null;
733 }
734
735 /**
736 * Returns a descriptor with id equal to instanceId.
Jack Hea355e5e2017-08-22 16:06:54 -0700737 *
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700738 * @hide
739 */
740 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
Jack Hea355e5e2017-08-22 16:06:54 -0700741 for (BluetoothGattService svc : mServices) {
742 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
743 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
744 if (desc.getInstanceId() == instanceId) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700745 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700746 }
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700747 }
748 }
749 }
750 return null;
751 }
752
753 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700754 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
755 * immediately if no Handler was provided.
756 */
757 private void runOrQueueCallback(final Runnable cb) {
758 if (mHandler == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700759 try {
760 cb.run();
761 } catch (Exception ex) {
762 Log.w(TAG, "Unhandled exception in callback", ex);
763 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700764 } else {
Jack Hea355e5e2017-08-22 16:06:54 -0700765 mHandler.post(cb);
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700766 }
767 }
768
769 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800770 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800771 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700772 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800773 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800774 *
775 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
776 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800777 * @param callback GATT callback handler that will receive asynchronous callbacks.
Jack Hea355e5e2017-08-22 16:06:54 -0700778 * @return If true, the callback will be called to notify success or failure, false on immediate
779 * error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800780 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700781 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800782 if (DBG) Log.d(TAG, "registerApp()");
783 if (mService == null) return false;
784
785 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700786 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800787 UUID uuid = UUID.randomUUID();
788 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
789
790 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700791 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800792 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700793 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800794 return false;
795 }
796
797 return true;
798 }
799
800 /**
801 * Unregister the current application and callbacks.
802 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800803 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800804 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
805 if (mService == null || mClientIf == 0) return;
806
807 try {
808 mCallback = null;
809 mService.unregisterClient(mClientIf);
810 mClientIf = 0;
811 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700812 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800813 }
814 }
815
816 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800817 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800818 *
819 * <p>The connection may not be established right away, but will be
820 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700821 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800822 * invoked when the connection state changes as a result of this function.
823 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700824 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 * the remote device, or rather passively scan and finalize the connection
826 * when the remote device is in range/available. Generally, the first ever
827 * connection to a device should be direct (autoConnect set to false) and
828 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800829 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800830 *
831 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
832 *
833 * @param device Remote device to connect to
Jack Hea355e5e2017-08-22 16:06:54 -0700834 * @param autoConnect Whether to directly connect to the remote device (false) or to
835 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800836 * @return true, if the connection attempt was initiated successfully
837 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700838 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Jack Hea355e5e2017-08-22 16:06:54 -0700839 Handler handler) {
840 if (DBG) {
841 Log.d(TAG,
842 "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
843 }
844 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800845 if (mConnState != CONN_STATE_IDLE) {
846 throw new IllegalStateException("Not idle");
847 }
848 mConnState = CONN_STATE_CONNECTING;
849 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700850
851 mAutoConnect = autoConnect;
852
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700853 if (!registerApp(callback, handler)) {
Jack Hea355e5e2017-08-22 16:06:54 -0700854 synchronized (mStateLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800855 mConnState = CONN_STATE_IDLE;
856 }
857 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800858 return false;
859 }
860
Sungki Kimd35167a2016-05-19 10:18:07 -0700861 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800862 return true;
863 }
864
865 /**
866 * Disconnects an established connection, or cancels a connection attempt
867 * currently in progress.
868 *
869 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800870 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800871 public void disconnect() {
872 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800873 if (mService == null || mClientIf == 0) return;
874
875 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800876 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800877 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700878 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800879 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700880 }
881
882 /**
883 * Connect back to remote device.
884 *
885 * <p>This method is used to re-connect to a remote device after the
886 * connection has been dropped. If the device is not in range, the
887 * re-connection will be triggered once the device is back in range.
888 *
889 * @return true, if the connection attempt was initiated successfully
890 */
891 public boolean connect() {
892 try {
Jack He13f52c82017-07-05 14:55:35 -0700893 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
894 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700895 return true;
896 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700897 Log.e(TAG, "", e);
Matthew Xie33ec9842013-04-03 00:29:27 -0700898 return false;
899 }
900 }
901
902 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800903 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskif752ace2017-03-22 22:44:09 -0700904 * recommendation, whether the PHY change will happen depends on other applications peferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800905 * local and remote controller capabilities. Controller can override these settings.
906 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700907 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800908 * if no PHY change happens. It is also triggered when remote device updates the PHY.
909 *
Jack Hea355e5e2017-08-22 16:06:54 -0700910 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
911 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
912 * BluetoothDevice#PHY_LE_CODED_MASK}.
913 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
914 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
915 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800916 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700917 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
918 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800919 */
920 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
921 try {
922 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700923 phyOptions);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800924 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700925 Log.e(TAG, "", e);
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800926 }
927 }
928
929 /**
930 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700931 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800932 */
933 public void readPhy() {
934 try {
935 mService.clientReadPhy(mClientIf, mDevice.getAddress());
936 } 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 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700942 * Return the remote bluetooth device this GATT client targets to
943 *
944 * @return remote bluetooth device
945 */
946 public BluetoothDevice getDevice() {
947 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800948 }
949
950 /**
951 * Discovers services offered by a remote device as well as their
952 * characteristics and descriptors.
953 *
954 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700955 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800956 * triggered. If the discovery was successful, the remote services can be
957 * retrieved using the {@link #getServices} function.
958 *
959 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
960 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800961 * @return true, if the remote service discovery has been started
962 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800963 public boolean discoverServices() {
964 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800965 if (mService == null || mClientIf == 0) return false;
966
967 mServices.clear();
968
969 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800970 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800971 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700972 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800973 return false;
974 }
975
976 return true;
977 }
978
979 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -0700980 * Discovers a service by UUID. This is exposed only for passing PTS tests.
981 * It should never be used by real applications. The service is not searched
982 * for characteristics and descriptors, or returned in any callback.
983 *
984 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
985 *
986 * @return true, if the remote service discovery has been started
987 * @hide
988 */
989 public boolean discoverServiceByUuid(UUID uuid) {
990 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
991 if (mService == null || mClientIf == 0) return false;
992
993 mServices.clear();
994
995 try {
996 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
997 } catch (RemoteException e) {
998 Log.e(TAG, "", e);
999 return false;
1000 }
1001 return true;
1002 }
1003
1004 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001005 * Returns a list of GATT services offered by the remote device.
1006 *
1007 * <p>This function requires that service discovery has been completed
1008 * for the given device.
1009 *
1010 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1011 *
Jack Hea355e5e2017-08-22 16:06:54 -07001012 * @return List of services on the remote device. Returns an empty list if service discovery has
1013 * not yet been performed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001014 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001015 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001016 List<BluetoothGattService> result =
1017 new ArrayList<BluetoothGattService>();
1018
1019 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001020 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001021 result.add(service);
1022 }
1023 }
1024
1025 return result;
1026 }
1027
1028 /**
1029 * Returns a {@link BluetoothGattService}, if the requested UUID is
1030 * supported by the remote device.
1031 *
1032 * <p>This function requires that service discovery has been completed
1033 * for the given device.
1034 *
1035 * <p>If multiple instances of the same service (as identified by UUID)
1036 * exist, the first instance of the service is returned.
1037 *
1038 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1039 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001040 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -07001041 * @return BluetoothGattService if supported, or null if the requested service is not offered by
1042 * the remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001043 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001044 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001045 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001046 if (service.getDevice().equals(mDevice) &&
Jack Hea355e5e2017-08-22 16:06:54 -07001047 service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001048 return service;
1049 }
1050 }
1051
1052 return null;
1053 }
1054
1055 /**
1056 * Reads the requested characteristic from the associated remote device.
1057 *
1058 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001059 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001060 * callback.
1061 *
1062 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1063 *
1064 * @param characteristic Characteristic to read from the remote device
1065 * @return true, if the read operation was initiated successfully
1066 */
1067 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
1068 if ((characteristic.getProperties() &
Jack Hea355e5e2017-08-22 16:06:54 -07001069 BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
1070 return false;
1071 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001072
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001073 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001074 if (mService == null || mClientIf == 0) return false;
1075
1076 BluetoothGattService service = characteristic.getService();
1077 if (service == null) return false;
1078
1079 BluetoothDevice device = service.getDevice();
1080 if (device == null) return false;
1081
Jack Hea355e5e2017-08-22 16:06:54 -07001082 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001083 if (mDeviceBusy) return false;
1084 mDeviceBusy = true;
1085 }
1086
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001087 try {
1088 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001089 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001090 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001091 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001092 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001093 return false;
1094 }
1095
1096 return true;
1097 }
1098
1099 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001100 * Reads the characteristic using its UUID from the associated remote device.
1101 *
1102 * <p>This is an asynchronous operation. The result of the read operation
1103 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1104 * callback.
1105 *
1106 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1107 *
1108 * @param uuid UUID of characteristic to read from the remote device
1109 * @return true, if the read operation was initiated successfully
1110 * @hide
1111 */
1112 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1113 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1114 if (mService == null || mClientIf == 0) return false;
1115
Jack Hea355e5e2017-08-22 16:06:54 -07001116 synchronized (mDeviceBusy) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001117 if (mDeviceBusy) return false;
1118 mDeviceBusy = true;
1119 }
1120
1121 try {
1122 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001123 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001124 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001125 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001126 mDeviceBusy = false;
1127 return false;
1128 }
1129
1130 return true;
1131 }
1132
1133
1134 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001135 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001136 *
1137 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001138 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001139 * reporting the result of the operation.
1140 *
1141 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1142 *
1143 * @param characteristic Characteristic to write on the remote device
1144 * @return true, if the write operation was initiated successfully
1145 */
1146 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1147 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack Hea355e5e2017-08-22 16:06:54 -07001148 && (characteristic.getProperties() &
1149 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
1150 return false;
1151 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001152
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001153 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001154 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001155
1156 BluetoothGattService service = characteristic.getService();
1157 if (service == null) return false;
1158
1159 BluetoothDevice device = service.getDevice();
1160 if (device == null) return false;
1161
Jack Hea355e5e2017-08-22 16:06:54 -07001162 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001163 if (mDeviceBusy) return false;
1164 mDeviceBusy = true;
1165 }
1166
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001167 try {
1168 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001169 characteristic.getInstanceId(), characteristic.getWriteType(),
1170 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001171 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001172 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001173 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001174 return false;
1175 }
1176
1177 return true;
1178 }
1179
1180 /**
1181 * Reads the value for a given descriptor from the associated remote device.
1182 *
1183 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001184 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001185 * triggered, signaling the result of the operation.
1186 *
1187 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1188 *
1189 * @param descriptor Descriptor value to read from the remote device
1190 * @return true, if the read operation was initiated successfully
1191 */
1192 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001193 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001194 if (mService == null || mClientIf == 0) return false;
1195
1196 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1197 if (characteristic == null) return false;
1198
1199 BluetoothGattService service = characteristic.getService();
1200 if (service == null) return false;
1201
1202 BluetoothDevice device = service.getDevice();
1203 if (device == null) return false;
1204
Jack Hea355e5e2017-08-22 16:06:54 -07001205 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001206 if (mDeviceBusy) return false;
1207 mDeviceBusy = true;
1208 }
1209
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001210 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001211 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001212 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001213 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001214 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001215 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001216 return false;
1217 }
1218
1219 return true;
1220 }
1221
1222 /**
1223 * Write the value of a given descriptor to the associated remote device.
1224 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001225 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001226 * triggered to report the result of the write operation.
1227 *
1228 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1229 *
1230 * @param descriptor Descriptor to write to the associated remote device
1231 * @return true, if the write operation was initiated successfully
1232 */
1233 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001234 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001235 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001236
1237 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1238 if (characteristic == null) return false;
1239
1240 BluetoothGattService service = characteristic.getService();
1241 if (service == null) return false;
1242
1243 BluetoothDevice device = service.getDevice();
1244 if (device == null) return false;
1245
Jack Hea355e5e2017-08-22 16:06:54 -07001246 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001247 if (mDeviceBusy) return false;
1248 mDeviceBusy = true;
1249 }
1250
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001251 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001252 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001253 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001254 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001255 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001256 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001257 return false;
1258 }
1259
1260 return true;
1261 }
1262
1263 /**
1264 * Initiates a reliable write transaction for a given remote device.
1265 *
1266 * <p>Once a reliable write transaction has been initiated, all calls
1267 * to {@link #writeCharacteristic} are sent to the remote device for
1268 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001269 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001270 * in response to every {@link #writeCharacteristic} call and is responsible
1271 * for verifying if the value has been transmitted accurately.
1272 *
1273 * <p>After all characteristics have been queued up and verified,
1274 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1275 * was not written correctly, calling {@link #abortReliableWrite} will
1276 * cancel the current transaction without commiting any values on the
1277 * remote device.
1278 *
1279 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1280 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001281 * @return true, if the reliable write transaction has been initiated
1282 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001283 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001284 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001285 if (mService == null || mClientIf == 0) return false;
1286
1287 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001288 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001289 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001290 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001291 return false;
1292 }
1293
1294 return true;
1295 }
1296
1297 /**
1298 * Executes a reliable write transaction for a given remote device.
1299 *
1300 * <p>This function will commit all queued up characteristic write
1301 * operations for a given remote device.
1302 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001303 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001304 * invoked to indicate whether the transaction has been executed correctly.
1305 *
1306 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1307 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001308 * @return true, if the request to execute the transaction has been sent
1309 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001310 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001311 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001312 if (mService == null || mClientIf == 0) return false;
1313
Jack Hea355e5e2017-08-22 16:06:54 -07001314 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001315 if (mDeviceBusy) return false;
1316 mDeviceBusy = true;
1317 }
1318
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001319 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001320 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001321 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001322 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001323 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001324 return false;
1325 }
1326
1327 return true;
1328 }
1329
1330 /**
1331 * Cancels a reliable write transaction for a given device.
1332 *
1333 * <p>Calling this function will discard all queued characteristic write
1334 * operations for a given remote device.
1335 *
1336 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001337 */
John Du48f8b5d2013-08-19 12:20:37 -07001338 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001339 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001340 if (mService == null || mClientIf == 0) return;
1341
1342 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001343 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001344 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001345 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001346 }
1347 }
1348
1349 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001350 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001351 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001352 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001353 public void abortReliableWrite(BluetoothDevice mDevice) {
1354 abortReliableWrite();
1355 }
1356
1357 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001358 * Enable or disable notifications/indications for a given characteristic.
1359 *
1360 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001361 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001362 * triggered if the remote device indicates that the given characteristic
1363 * has changed.
1364 *
1365 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1366 *
1367 * @param characteristic The characteristic for which to enable notifications
1368 * @param enable Set to true to enable notifications/indications
1369 * @return true, if the requested notification status was set successfully
1370 */
1371 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001372 boolean enable) {
1373 if (DBG) {
1374 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1375 + " enable: " + enable);
1376 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001377 if (mService == null || mClientIf == 0) return false;
1378
1379 BluetoothGattService service = characteristic.getService();
1380 if (service == null) return false;
1381
1382 BluetoothDevice device = service.getDevice();
1383 if (device == null) return false;
1384
1385 try {
1386 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001387 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001388 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001389 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001390 return false;
1391 }
1392
1393 return true;
1394 }
1395
1396 /**
1397 * Clears the internal cache and forces a refresh of the services from the
1398 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001399 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001400 * @hide
1401 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001402 public boolean refresh() {
1403 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001404 if (mService == null || mClientIf == 0) return false;
1405
1406 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001407 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001408 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001409 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001410 return false;
1411 }
1412
1413 return true;
1414 }
1415
1416 /**
1417 * Read the RSSI for a connected remote device.
1418 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001419 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001420 * invoked when the RSSI value has been read.
1421 *
1422 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1423 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001424 * @return true, if the RSSI value has been requested successfully
1425 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001426 public boolean readRemoteRssi() {
1427 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001428 if (mService == null || mClientIf == 0) return false;
1429
1430 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001431 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001432 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001433 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001434 return false;
1435 }
1436
1437 return true;
1438 }
1439
1440 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001441 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001442 *
1443 * <p>When performing a write request operation (write without response),
1444 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001445 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001446 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001447 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001448 * whether this operation was successful.
1449 *
1450 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1451 *
1452 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001453 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001454 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001455 if (DBG) {
1456 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1457 + " mtu: " + mtu);
1458 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001459 if (mService == null || mClientIf == 0) return false;
1460
1461 try {
1462 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1463 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001464 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001465 return false;
1466 }
1467
1468 return true;
1469 }
1470
1471 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001472 * Request a connection parameter update.
1473 *
1474 * <p>This function will send a connection parameter update request to the
1475 * remote device.
1476 *
Jack Hea355e5e2017-08-22 16:06:54 -07001477 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1478 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1479 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1480 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001481 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001482 public boolean requestConnectionPriority(int connectionPriority) {
1483 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
Jack Hea355e5e2017-08-22 16:06:54 -07001484 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001485 throw new IllegalArgumentException("connectionPriority not within valid range");
1486 }
1487
Andre Eisenbach4072da02014-08-19 17:58:55 -07001488 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001489 if (mService == null || mClientIf == 0) return false;
1490
1491 try {
1492 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1493 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001494 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001495 return false;
1496 }
1497
1498 return true;
1499 }
1500
1501 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001502 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1503 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001504 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001505 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001506 */
1507 @Override
1508 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001509 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001510 }
1511
1512 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001513 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1514 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001515 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001516 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001517 */
1518 @Override
1519 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001520 throw new UnsupportedOperationException
Jack Hea355e5e2017-08-22 16:06:54 -07001521 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001522 }
1523
1524 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001525 * Not supported - please use
1526 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1527 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001528 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001529 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001530 */
1531 @Override
1532 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001533 throw new UnsupportedOperationException
Jack Hea355e5e2017-08-22 16:06:54 -07001534 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001535 }
1536}