blob: 759d772920baa15fd5593aa3d700665dd68c3537 [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 {
Jack He2992cd02017-08-22 21:21:23 -0700355 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
356 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700357 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 {
Jack He2992cd02017-08-22 21:21:23 -0700415 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
416 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700417 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 {
Jack He2992cd02017-08-22 21:21:23 -0700498 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
499 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700500 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 {
Jack He2992cd02017-08-22 21:21:23 -0700546 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
547 ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jack Hea355e5e2017-08-22 16:06:54 -0700548 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) {
Jack He2992cd02017-08-22 21:21:23 -0700604 Log.d(TAG, "onReadRemoteRssi() - Device=" + address
605 + " rssi=" + rssi + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700606 }
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) {
Jack He2992cd02017-08-22 21:21:23 -0700627 Log.d(TAG, "onConfigureMTU() - Device=" + address
628 + " mtu=" + mtu + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700629 }
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) {
Jack He2992cd02017-08-22 21:21:23 -0700652 Log.d(TAG, "onConnectionUpdated() - Device=" + address
653 + " interval=" + interval + " latency=" + latency
654 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700655 }
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) {
Jack He2992cd02017-08-22 21:21:23 -0700707 if (svc.getDevice().equals(device)
708 && 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) {
Jack He2992cd02017-08-22 21:21:23 -07001046 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001047 return service;
1048 }
1049 }
1050
1051 return null;
1052 }
1053
1054 /**
1055 * Reads the requested characteristic from the associated remote device.
1056 *
1057 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001058 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001059 * callback.
1060 *
1061 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1062 *
1063 * @param characteristic Characteristic to read from the remote device
1064 * @return true, if the read operation was initiated successfully
1065 */
1066 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
Jack He2992cd02017-08-22 21:21:23 -07001067 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001068 return false;
1069 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001070
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001071 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001072 if (mService == null || mClientIf == 0) return false;
1073
1074 BluetoothGattService service = characteristic.getService();
1075 if (service == null) return false;
1076
1077 BluetoothDevice device = service.getDevice();
1078 if (device == null) return false;
1079
Jack Hea355e5e2017-08-22 16:06:54 -07001080 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001081 if (mDeviceBusy) return false;
1082 mDeviceBusy = true;
1083 }
1084
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001085 try {
1086 mService.readCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001087 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001088 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001089 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001090 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001091 return false;
1092 }
1093
1094 return true;
1095 }
1096
1097 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001098 * Reads the characteristic using its UUID from the associated remote device.
1099 *
1100 * <p>This is an asynchronous operation. The result of the read operation
1101 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1102 * callback.
1103 *
1104 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1105 *
1106 * @param uuid UUID of characteristic to read from the remote device
1107 * @return true, if the read operation was initiated successfully
1108 * @hide
1109 */
1110 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1111 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1112 if (mService == null || mClientIf == 0) return false;
1113
Jack Hea355e5e2017-08-22 16:06:54 -07001114 synchronized (mDeviceBusy) {
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001115 if (mDeviceBusy) return false;
1116 mDeviceBusy = true;
1117 }
1118
1119 try {
1120 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001121 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001122 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001123 Log.e(TAG, "", e);
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001124 mDeviceBusy = false;
1125 return false;
1126 }
1127
1128 return true;
1129 }
1130
1131
1132 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001133 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001134 *
1135 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001136 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001137 * reporting the result of the operation.
1138 *
1139 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1140 *
1141 * @param characteristic Characteristic to write on the remote device
1142 * @return true, if the write operation was initiated successfully
1143 */
1144 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1145 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
Jack He2992cd02017-08-22 21:21:23 -07001146 && (characteristic.getProperties()
1147 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
Jack Hea355e5e2017-08-22 16:06:54 -07001148 return false;
1149 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001150
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001151 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001152 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001153
1154 BluetoothGattService service = characteristic.getService();
1155 if (service == null) return false;
1156
1157 BluetoothDevice device = service.getDevice();
1158 if (device == null) return false;
1159
Jack Hea355e5e2017-08-22 16:06:54 -07001160 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001161 if (mDeviceBusy) return false;
1162 mDeviceBusy = true;
1163 }
1164
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001165 try {
1166 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001167 characteristic.getInstanceId(), characteristic.getWriteType(),
1168 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001169 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001170 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001171 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001172 return false;
1173 }
1174
1175 return true;
1176 }
1177
1178 /**
1179 * Reads the value for a given descriptor from the associated remote device.
1180 *
1181 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001182 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001183 * triggered, signaling the result of the operation.
1184 *
1185 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1186 *
1187 * @param descriptor Descriptor value to read from the remote device
1188 * @return true, if the read operation was initiated successfully
1189 */
1190 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001191 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001192 if (mService == null || mClientIf == 0) return false;
1193
1194 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1195 if (characteristic == null) return false;
1196
1197 BluetoothGattService service = characteristic.getService();
1198 if (service == null) return false;
1199
1200 BluetoothDevice device = service.getDevice();
1201 if (device == null) return false;
1202
Jack Hea355e5e2017-08-22 16:06:54 -07001203 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001204 if (mDeviceBusy) return false;
1205 mDeviceBusy = true;
1206 }
1207
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001208 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001209 mService.readDescriptor(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001210 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001211 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001212 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001213 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001214 return false;
1215 }
1216
1217 return true;
1218 }
1219
1220 /**
1221 * Write the value of a given descriptor to the associated remote device.
1222 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001223 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001224 * triggered to report the result of the write operation.
1225 *
1226 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1227 *
1228 * @param descriptor Descriptor to write to the associated remote device
1229 * @return true, if the write operation was initiated successfully
1230 */
1231 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001232 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001233 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001234
1235 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1236 if (characteristic == null) return false;
1237
1238 BluetoothGattService service = characteristic.getService();
1239 if (service == null) return false;
1240
1241 BluetoothDevice device = service.getDevice();
1242 if (device == null) return false;
1243
Jack Hea355e5e2017-08-22 16:06:54 -07001244 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001245 if (mDeviceBusy) return false;
1246 mDeviceBusy = true;
1247 }
1248
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001249 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001250 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jack Hea355e5e2017-08-22 16:06:54 -07001251 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001252 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001253 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001254 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001255 return false;
1256 }
1257
1258 return true;
1259 }
1260
1261 /**
1262 * Initiates a reliable write transaction for a given remote device.
1263 *
1264 * <p>Once a reliable write transaction has been initiated, all calls
1265 * to {@link #writeCharacteristic} are sent to the remote device for
1266 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001267 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001268 * in response to every {@link #writeCharacteristic} call and is responsible
1269 * for verifying if the value has been transmitted accurately.
1270 *
1271 * <p>After all characteristics have been queued up and verified,
1272 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1273 * was not written correctly, calling {@link #abortReliableWrite} will
1274 * cancel the current transaction without commiting any values on the
1275 * remote device.
1276 *
1277 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1278 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001279 * @return true, if the reliable write transaction has been initiated
1280 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001281 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001282 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001283 if (mService == null || mClientIf == 0) return false;
1284
1285 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001286 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001287 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001288 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001289 return false;
1290 }
1291
1292 return true;
1293 }
1294
1295 /**
1296 * Executes a reliable write transaction for a given remote device.
1297 *
1298 * <p>This function will commit all queued up characteristic write
1299 * operations for a given remote device.
1300 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001301 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001302 * invoked to indicate whether the transaction has been executed correctly.
1303 *
1304 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1305 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001306 * @return true, if the request to execute the transaction has been sent
1307 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001308 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001309 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001310 if (mService == null || mClientIf == 0) return false;
1311
Jack Hea355e5e2017-08-22 16:06:54 -07001312 synchronized (mDeviceBusy) {
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001313 if (mDeviceBusy) return false;
1314 mDeviceBusy = true;
1315 }
1316
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001317 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001318 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001319 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001320 Log.e(TAG, "", e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001321 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001322 return false;
1323 }
1324
1325 return true;
1326 }
1327
1328 /**
1329 * Cancels a reliable write transaction for a given device.
1330 *
1331 * <p>Calling this function will discard all queued characteristic write
1332 * operations for a given remote device.
1333 *
1334 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001335 */
John Du48f8b5d2013-08-19 12:20:37 -07001336 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001337 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001338 if (mService == null || mClientIf == 0) return;
1339
1340 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001341 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001342 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001343 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001344 }
1345 }
1346
1347 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001348 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001349 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001350 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001351 public void abortReliableWrite(BluetoothDevice mDevice) {
1352 abortReliableWrite();
1353 }
1354
1355 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001356 * Enable or disable notifications/indications for a given characteristic.
1357 *
1358 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001359 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001360 * triggered if the remote device indicates that the given characteristic
1361 * has changed.
1362 *
1363 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1364 *
1365 * @param characteristic The characteristic for which to enable notifications
1366 * @param enable Set to true to enable notifications/indications
1367 * @return true, if the requested notification status was set successfully
1368 */
1369 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
Jack Hea355e5e2017-08-22 16:06:54 -07001370 boolean enable) {
1371 if (DBG) {
1372 Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1373 + " enable: " + enable);
1374 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001375 if (mService == null || mClientIf == 0) return false;
1376
1377 BluetoothGattService service = characteristic.getService();
1378 if (service == null) return false;
1379
1380 BluetoothDevice device = service.getDevice();
1381 if (device == null) return false;
1382
1383 try {
1384 mService.registerForNotification(mClientIf, device.getAddress(),
Jack Hea355e5e2017-08-22 16:06:54 -07001385 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001386 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001387 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001388 return false;
1389 }
1390
1391 return true;
1392 }
1393
1394 /**
1395 * Clears the internal cache and forces a refresh of the services from the
1396 * remote device.
Jack Hea355e5e2017-08-22 16:06:54 -07001397 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001398 * @hide
1399 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001400 public boolean refresh() {
1401 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001402 if (mService == null || mClientIf == 0) return false;
1403
1404 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001405 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001406 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001407 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001408 return false;
1409 }
1410
1411 return true;
1412 }
1413
1414 /**
1415 * Read the RSSI for a connected remote device.
1416 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001417 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001418 * invoked when the RSSI value has been read.
1419 *
1420 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1421 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001422 * @return true, if the RSSI value has been requested successfully
1423 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001424 public boolean readRemoteRssi() {
1425 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001426 if (mService == null || mClientIf == 0) return false;
1427
1428 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001429 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001430 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001431 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001432 return false;
1433 }
1434
1435 return true;
1436 }
1437
1438 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001439 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001440 *
1441 * <p>When performing a write request operation (write without response),
1442 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001443 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001444 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001445 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001446 * whether this operation was successful.
1447 *
1448 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1449 *
1450 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001451 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001452 public boolean requestMtu(int mtu) {
Jack Hea355e5e2017-08-22 16:06:54 -07001453 if (DBG) {
1454 Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1455 + " mtu: " + mtu);
1456 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001457 if (mService == null || mClientIf == 0) return false;
1458
1459 try {
1460 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1461 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001462 Log.e(TAG, "", e);
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001463 return false;
1464 }
1465
1466 return true;
1467 }
1468
1469 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001470 * Request a connection parameter update.
1471 *
1472 * <p>This function will send a connection parameter update request to the
1473 * remote device.
1474 *
Jack Hea355e5e2017-08-22 16:06:54 -07001475 * @param connectionPriority Request a specific connection priority. Must be one of {@link
1476 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1477 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
1478 * @throws IllegalArgumentException If the parameters are outside of their specified range.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001479 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001480 public boolean requestConnectionPriority(int connectionPriority) {
Jack He2992cd02017-08-22 21:21:23 -07001481 if (connectionPriority < CONNECTION_PRIORITY_BALANCED
1482 || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001483 throw new IllegalArgumentException("connectionPriority not within valid range");
1484 }
1485
Andre Eisenbach4072da02014-08-19 17:58:55 -07001486 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001487 if (mService == null || mClientIf == 0) return false;
1488
1489 try {
1490 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1491 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -07001492 Log.e(TAG, "", e);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001493 return false;
1494 }
1495
1496 return true;
1497 }
1498
1499 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001500 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1501 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001502 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001503 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001504 */
1505 @Override
1506 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001507 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001508 }
1509
1510 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001511 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1512 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001513 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001514 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001515 */
1516 @Override
1517 public List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -07001518 throw new UnsupportedOperationException(
1519 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001520 }
1521
1522 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001523 * Not supported - please use
1524 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1525 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001526 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001527 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001528 */
1529 @Override
1530 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -07001531 throw new UnsupportedOperationException(
1532 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001533 }
1534}