blob: b596dd6b2c93fbaa5a251f9b9518507b7fa55f41 [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 Pawlowski05c15782017-09-13 09:33:34 -070045 private volatile BluetoothGattCallback mCallback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070046 private Handler mHandler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080047 private int mClientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -080048 private BluetoothDevice mDevice;
49 private boolean mAutoConnect;
Jacky Cheung3854e222016-10-20 13:55:21 -070050 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080051 private int mConnState;
52 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070053 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070054 private int mTransport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080055 private int mPhy;
Jack He13f52c82017-07-05 14:55:35 -070056 private boolean mOpportunistic;
Matthew Xieddf7e472013-03-01 18:41:02 -080057
Jacky Cheung3854e222016-10-20 13:55:21 -070058 private static final int AUTH_RETRY_STATE_IDLE = 0;
59 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
60 private static final int AUTH_RETRY_STATE_MITM = 2;
61
Matthew Xieddf7e472013-03-01 18:41:02 -080062 private static final int CONN_STATE_IDLE = 0;
63 private static final int CONN_STATE_CONNECTING = 1;
64 private static final int CONN_STATE_CONNECTED = 2;
65 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070066 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080067
68 private List<BluetoothGattService> mServices;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_SUCCESS = 0;
72
Matthew Xieddf7e472013-03-01 18:41:02 -080073 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080074 public static final int GATT_READ_NOT_PERMITTED = 0x2;
75
Matthew Xieddf7e472013-03-01 18:41:02 -080076 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080077 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
78
79 /** Insufficient authentication for a given operation */
80 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
81
82 /** The given request is not supported */
83 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
84
85 /** Insufficient encryption for a given operation */
86 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
87
88 /** A read or write operation was requested with an invalid offset */
89 public static final int GATT_INVALID_OFFSET = 0x7;
90
91 /** A write operation exceeds the maximum length of the attribute */
92 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
93
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070094 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070095 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
96
Matthew Xie90ca8072013-05-28 21:06:50 +000097 /** A GATT operation failed, errors other than the above */
98 public static final int GATT_FAILURE = 0x101;
99
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800100 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700101 * Connection paramter update - Use the connection paramters recommended by the
102 * Bluetooth SIG. This is the default value if no connection parameter update
103 * is requested.
104 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700105 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700106
107 /**
108 * Connection paramter update - Request a high priority, low latency connection.
109 * An application should only request high priority connection paramters to transfer
110 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700111 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112 * to reduce energy use.
113 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700114 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700115
116 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700117 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700118
119 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800120 * No authentication required.
121 * @hide
122 */
123 /*package*/ static final int AUTHENTICATION_NONE = 0;
124
125 /**
126 * Authentication requested; no man-in-the-middle protection required.
127 * @hide
128 */
129 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
130
131 /**
132 * Authentication with man-in-the-middle protection requested.
133 * @hide
134 */
135 /*package*/ static final int AUTHENTICATION_MITM = 2;
136
137 /**
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700138 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800139 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -0700140 private final IBluetoothGattCallback mBluetoothGattCallback =
141 new IBluetoothGattCallback.Stub() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800142 /**
143 * Application interface registered - app is ready to go
144 * @hide
145 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700146 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800147 public void onClientRegistered(int status, int clientIf) {
148 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
149 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800150 if (VDBG) {
151 synchronized(mStateLock) {
152 if (mConnState != CONN_STATE_CONNECTING) {
153 Log.e(TAG, "Bad connection state: " + mConnState);
154 }
155 }
156 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800157 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800158 if (status != GATT_SUCCESS) {
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700159 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700160 @Override
161 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700162 final BluetoothGattCallback callback = mCallback;
163 if (callback != null) {
164 callback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700165 BluetoothProfile.STATE_DISCONNECTED);
166 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700167 }
168 });
169
Matthew Xieddf7e472013-03-01 18:41:02 -0800170 synchronized(mStateLock) {
171 mConnState = CONN_STATE_IDLE;
172 }
173 return;
174 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800175 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800176 mService.clientConnect(mClientIf, mDevice.getAddress(),
Jack He13f52c82017-07-05 14:55:35 -0700177 !mAutoConnect, mTransport, mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800178 } catch (RemoteException e) {
179 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800180 }
181 }
182
183 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800184 * Phy update callback
185 * @hide
186 */
187 @Override
188 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
189 if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status
190 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
191 if (!address.equals(mDevice.getAddress())) {
192 return;
193 }
194
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700195 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700196 @Override
197 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700198 final BluetoothGattCallback callback = mCallback;
199 if (callback != null) {
200 callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700201 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700202 }
203 });
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800204 }
205
206 /**
207 * Phy read callback
208 * @hide
209 */
210 @Override
211 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
212 if (DBG) Log.d(TAG, "onPhyRead() - status=" + status
213 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
214 if (!address.equals(mDevice.getAddress())) {
215 return;
216 }
217
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700218 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700219 @Override
220 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700221 final BluetoothGattCallback callback = mCallback;
222 if (callback != null) {
223 callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700224 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700225 }
226 });
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800227 }
228
229 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800230 * Client connection state changed
231 * @hide
232 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700233 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800234 public void onClientConnectionState(int status, int clientIf,
235 boolean connected, String address) {
236 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
237 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800238 if (!address.equals(mDevice.getAddress())) {
239 return;
240 }
241 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
242 BluetoothProfile.STATE_DISCONNECTED;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700243
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700244 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700245 @Override
246 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700247 final BluetoothGattCallback callback = mCallback;
248 if (callback != null) {
249 callback.onConnectionStateChange(BluetoothGatt.this, status,
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700250 profileState);
251 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700252 }
253 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800254
255 synchronized(mStateLock) {
256 if (connected) {
257 mConnState = CONN_STATE_CONNECTED;
258 } else {
259 mConnState = CONN_STATE_IDLE;
260 }
261 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700262
263 synchronized(mDeviceBusy) {
264 mDeviceBusy = false;
265 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800266 }
267
268 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800269 * Remote search has been completed.
270 * The internal object structure should now reflect the state
271 * of the remote device database. Let the application know that
272 * we are done at this point.
273 * @hide
274 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700275 @Override
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800276 public void onSearchComplete(String address, List<BluetoothGattService> services,
277 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800278 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800279 if (!address.equals(mDevice.getAddress())) {
280 return;
281 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800282
283 for (BluetoothGattService s : services) {
284 //services we receive don't have device set properly.
285 s.setDevice(mDevice);
286 }
287
288 mServices.addAll(services);
289
290 // Fix references to included services, as they doesn't point to right objects.
291 for (BluetoothGattService fixedService : mServices) {
292 ArrayList<BluetoothGattService> includedServices =
293 new ArrayList(fixedService.getIncludedServices());
294 fixedService.getIncludedServices().clear();
295
296 for(BluetoothGattService brokenRef : includedServices) {
297 BluetoothGattService includedService = getService(mDevice,
298 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
299 if (includedService != null) {
300 fixedService.addIncludedService(includedService);
301 } else {
302 Log.e(TAG, "Broken GATT database: can't find included service.");
303 }
304 }
305 }
306
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700307 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700308 @Override
309 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700310 final BluetoothGattCallback callback = mCallback;
311 if (callback != null) {
312 callback.onServicesDiscovered(BluetoothGatt.this, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700313 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700314 }
315 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 }
317
318 /**
319 * Remote characteristic has been read.
320 * Updates the internal value.
321 * @hide
322 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700323 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700324 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700325 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700326 + " handle=" + handle + " Status=" + status);
327
Matthew Xieddf7e472013-03-01 18:41:02 -0800328 if (!address.equals(mDevice.getAddress())) {
329 return;
330 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700331
332 synchronized(mDeviceBusy) {
333 mDeviceBusy = false;
334 }
335
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800336 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
337 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700338 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800339 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700340 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
341 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
342 mService.readCharacteristic(mClientIf, address, handle, authReq);
343 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800344 return;
345 } catch (RemoteException e) {
346 Log.e(TAG,"",e);
347 }
348 }
349
Jacky Cheung3854e222016-10-20 13:55:21 -0700350 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800351
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700352 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
353 if (characteristic == null) {
354 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
355 return;
356 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700358 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700359 @Override
360 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700361 final BluetoothGattCallback callback = mCallback;
362 if (callback != null) {
Jakub Pawlowski93703a02017-09-12 14:48:30 -0700363 if (status == 0) characteristic.setValue(value);
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700364 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700365 status);
366 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700367 }
368 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800369 }
370
371 /**
372 * Characteristic has been written to the remote device.
373 * Let the app know how we did...
374 * @hide
375 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700376 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700377 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700378 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700379 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800380
Matthew Xieddf7e472013-03-01 18:41:02 -0800381 if (!address.equals(mDevice.getAddress())) {
382 return;
383 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700384
385 synchronized(mDeviceBusy) {
386 mDeviceBusy = false;
387 }
388
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700389 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800390 if (characteristic == null) return;
391
392 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
393 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700394 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800395 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700396 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
397 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700398 mService.writeCharacteristic(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700399 characteristic.getWriteType(), authReq, characteristic.getValue());
400 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800401 return;
402 } catch (RemoteException e) {
403 Log.e(TAG,"",e);
404 }
405 }
406
Jacky Cheung3854e222016-10-20 13:55:21 -0700407 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800408
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700409 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700410 @Override
411 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700412 final BluetoothGattCallback callback = mCallback;
413 if (callback != null) {
414 callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700415 status);
416 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700417 }
418 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800419 }
420
421 /**
422 * Remote characteristic has been updated.
423 * Updates the internal value.
424 * @hide
425 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700426 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700427 public void onNotify(String address, int handle, byte[] value) {
428 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800429
Matthew Xieddf7e472013-03-01 18:41:02 -0800430 if (!address.equals(mDevice.getAddress())) {
431 return;
432 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800433
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700434 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800435 if (characteristic == null) return;
436
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700437 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700438 @Override
439 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700440 final BluetoothGattCallback callback = mCallback;
441 if (callback != null) {
Jakub Pawlowski93703a02017-09-12 14:48:30 -0700442 characteristic.setValue(value);
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700443 callback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700444 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700445 }
446 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800447 }
448
449 /**
450 * Descriptor has been read.
451 * @hide
452 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700453 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700454 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
455 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800456
Matthew Xieddf7e472013-03-01 18:41:02 -0800457 if (!address.equals(mDevice.getAddress())) {
458 return;
459 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700460
461 synchronized(mDeviceBusy) {
462 mDeviceBusy = false;
463 }
464
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700465 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800466 if (descriptor == null) return;
467
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800468
469 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
470 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700471 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800472 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700473 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
474 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
475 mService.readDescriptor(mClientIf, address, handle, authReq);
476 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700477 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800478 } catch (RemoteException e) {
479 Log.e(TAG,"",e);
480 }
481 }
482
Jacky Cheung3854e222016-10-20 13:55:21 -0700483 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800484
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700485 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700486 @Override
487 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700488 final BluetoothGattCallback callback = mCallback;
489 if (callback != null) {
Jakub Pawlowski93703a02017-09-12 14:48:30 -0700490 if (status == 0) descriptor.setValue(value);
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700491 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700492 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700493 }
494 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800495 }
496
497 /**
498 * Descriptor write operation complete.
499 * @hide
500 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700501 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700502 public void onDescriptorWrite(String address, int status, int handle) {
503 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504
Matthew Xieddf7e472013-03-01 18:41:02 -0800505 if (!address.equals(mDevice.getAddress())) {
506 return;
507 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700508
509 synchronized(mDeviceBusy) {
510 mDeviceBusy = false;
511 }
512
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700513 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800514 if (descriptor == null) return;
515
516 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
517 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700518 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700520 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
521 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700522 mService.writeDescriptor(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700523 authReq, descriptor.getValue());
524 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700525 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800526 } catch (RemoteException e) {
527 Log.e(TAG,"",e);
528 }
529 }
530
Jacky Cheung3854e222016-10-20 13:55:21 -0700531 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800532
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700533 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700534 @Override
535 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700536 final BluetoothGattCallback callback = mCallback;
537 if (callback != null) {
538 callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700539 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700540 }
541 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800542 }
543
544 /**
545 * Prepared write transaction completed (or aborted)
546 * @hide
547 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700548 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800549 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700550 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800551 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800552 if (!address.equals(mDevice.getAddress())) {
553 return;
554 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700555
556 synchronized(mDeviceBusy) {
557 mDeviceBusy = false;
558 }
559
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700560 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700561 @Override
562 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700563 final BluetoothGattCallback callback = mCallback;
564 if (callback != null) {
565 callback.onReliableWriteCompleted(BluetoothGatt.this, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700566 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700567 }
568 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800569 }
570
571 /**
572 * Remote device RSSI has been read
573 * @hide
574 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700575 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800576 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700577 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800578 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800579 if (!address.equals(mDevice.getAddress())) {
580 return;
581 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700582 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700583 @Override
584 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700585 final BluetoothGattCallback callback = mCallback;
586 if (callback != null) {
587 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700588 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700589 }
590 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800591 }
Wei Wangf3055892014-03-11 22:22:41 -0700592
593 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700594 * Callback invoked when the MTU for a given connection changes
595 * @hide
596 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700597 @Override
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700598 public void onConfigureMTU(String address, int mtu, int status) {
599 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
600 " mtu=" + mtu + " status=" + status);
601 if (!address.equals(mDevice.getAddress())) {
602 return;
603 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700604
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700605 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700606 @Override
607 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700608 final BluetoothGattCallback callback = mCallback;
609 if (callback != null) {
610 callback.onMtuChanged(BluetoothGatt.this, mtu, status);
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700611 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700612 }
613 });
Wei Wangf3055892014-03-11 22:22:41 -0700614 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700615
616 /**
617 * Callback invoked when the given connection is updated
618 * @hide
619 */
620 @Override
621 public void onConnectionUpdated(String address, int interval, int latency,
622 int timeout, int status) {
623 if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address +
624 " interval=" + interval + " latency=" + latency +
625 " timeout=" + timeout + " status=" + status);
626 if (!address.equals(mDevice.getAddress())) {
627 return;
628 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700629
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700630 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700631 @Override
632 public void run() {
Jakub Pawlowski05c15782017-09-13 09:33:34 -0700633 final BluetoothGattCallback callback = mCallback;
634 if (callback != null) {
635 callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700636 timeout, status);
637 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700638 }
639 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700640 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800641 };
642
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700643 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jack He13f52c82017-07-05 14:55:35 -0700644 int transport, boolean opportunistic, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800645 mService = iGatt;
646 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700647 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800648 mPhy = phy;
Jack He13f52c82017-07-05 14:55:35 -0700649 mOpportunistic = opportunistic;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800650 mServices = new ArrayList<BluetoothGattService>();
651
Matthew Xieddf7e472013-03-01 18:41:02 -0800652 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700653 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800654 }
655
656 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700657 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700658 *
659 * Application should call this method as early as possible after it is done with
660 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800661 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700662 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800663 if (DBG) Log.d(TAG, "close()");
664
665 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700666 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700667 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800668 }
669
670 /**
671 * Returns a service by UUID, instance and type.
672 * @hide
673 */
674 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
675 int instanceId, int type) {
676 for(BluetoothGattService svc : mServices) {
677 if (svc.getDevice().equals(device) &&
678 svc.getType() == type &&
679 svc.getInstanceId() == instanceId &&
680 svc.getUuid().equals(uuid)) {
681 return svc;
682 }
683 }
684 return null;
685 }
686
687
688 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700689 * Returns a characteristic with id equal to instanceId.
690 * @hide
691 */
692 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
693 for(BluetoothGattService svc : mServices) {
694 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700695 if (charac.getInstanceId() == instanceId)
696 return charac;
697 }
698 }
699 return null;
700 }
701
702 /**
703 * Returns a descriptor with id equal to instanceId.
704 * @hide
705 */
706 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
707 for(BluetoothGattService svc : mServices) {
708 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
709 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
710 if (desc.getInstanceId() == instanceId)
711 return desc;
712 }
713 }
714 }
715 return null;
716 }
717
718 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700719 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
720 * immediately if no Handler was provided.
721 */
722 private void runOrQueueCallback(final Runnable cb) {
723 if (mHandler == null) {
724 try {
725 cb.run();
726 } catch (Exception ex) {
727 Log.w(TAG, "Unhandled exception in callback", ex);
728 }
729 } else {
730 mHandler.post(cb);
731 }
732 }
733
734 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800735 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800736 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700737 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800738 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800739 *
740 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
741 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800742 * @param callback GATT callback handler that will receive asynchronous callbacks.
743 * @return If true, the callback will be called to notify success or failure,
744 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800745 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700746 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800747 if (DBG) Log.d(TAG, "registerApp()");
748 if (mService == null) return false;
749
750 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700751 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800752 UUID uuid = UUID.randomUUID();
753 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
754
755 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700756 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800757 } catch (RemoteException e) {
758 Log.e(TAG,"",e);
759 return false;
760 }
761
762 return true;
763 }
764
765 /**
766 * Unregister the current application and callbacks.
767 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800768 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800769 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
770 if (mService == null || mClientIf == 0) return;
771
772 try {
773 mCallback = null;
774 mService.unregisterClient(mClientIf);
775 mClientIf = 0;
776 } catch (RemoteException e) {
777 Log.e(TAG,"",e);
778 }
779 }
780
781 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800782 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800783 *
784 * <p>The connection may not be established right away, but will be
785 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700786 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800787 * invoked when the connection state changes as a result of this function.
788 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700789 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 * the remote device, or rather passively scan and finalize the connection
791 * when the remote device is in range/available. Generally, the first ever
792 * connection to a device should be direct (autoConnect set to false) and
793 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800794 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800795 *
796 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
797 *
798 * @param device Remote device to connect to
799 * @param autoConnect Whether to directly connect to the remote device (false)
800 * or to automatically connect as soon as the remote
801 * device becomes available (true).
802 * @return true, if the connection attempt was initiated successfully
803 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700804 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
805 Handler handler) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800806 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
807 synchronized(mStateLock) {
808 if (mConnState != CONN_STATE_IDLE) {
809 throw new IllegalStateException("Not idle");
810 }
811 mConnState = CONN_STATE_CONNECTING;
812 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700813
814 mAutoConnect = autoConnect;
815
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700816 if (!registerApp(callback, handler)) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800817 synchronized(mStateLock) {
818 mConnState = CONN_STATE_IDLE;
819 }
820 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800821 return false;
822 }
823
Sungki Kimd35167a2016-05-19 10:18:07 -0700824 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 return true;
826 }
827
828 /**
829 * Disconnects an established connection, or cancels a connection attempt
830 * currently in progress.
831 *
832 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800833 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800834 public void disconnect() {
835 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800836 if (mService == null || mClientIf == 0) return;
837
838 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800839 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800840 } catch (RemoteException e) {
841 Log.e(TAG,"",e);
842 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700843 }
844
845 /**
846 * Connect back to remote device.
847 *
848 * <p>This method is used to re-connect to a remote device after the
849 * connection has been dropped. If the device is not in range, the
850 * re-connection will be triggered once the device is back in range.
851 *
852 * @return true, if the connection attempt was initiated successfully
853 */
854 public boolean connect() {
855 try {
Jack He13f52c82017-07-05 14:55:35 -0700856 mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
857 mOpportunistic, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700858 return true;
859 } catch (RemoteException e) {
860 Log.e(TAG,"",e);
861 return false;
862 }
863 }
864
865 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800866 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskif752ace2017-03-22 22:44:09 -0700867 * recommendation, whether the PHY change will happen depends on other applications peferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800868 * local and remote controller capabilities. Controller can override these settings.
869 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700870 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800871 * if no PHY change happens. It is also triggered when remote device updates the PHY.
872 *
873 * @param txPhy preferred transmitter PHY. Bitwise OR of any of
Jakub Pawlowskia5151372017-04-12 08:51:22 -0700874 * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK},
875 * and {@link BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800876 * @param rxPhy preferred receiver PHY. Bitwise OR of any of
Jakub Pawlowskia5151372017-04-12 08:51:22 -0700877 * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK},
878 * and {@link BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800879 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
880 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
881 * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
882 */
883 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
884 try {
885 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
886 phyOptions);
887 } catch (RemoteException e) {
888 Log.e(TAG,"",e);
889 }
890 }
891
892 /**
893 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700894 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800895 */
896 public void readPhy() {
897 try {
898 mService.clientReadPhy(mClientIf, mDevice.getAddress());
899 } catch (RemoteException e) {
900 Log.e(TAG,"",e);
901 }
902 }
903
904 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700905 * Return the remote bluetooth device this GATT client targets to
906 *
907 * @return remote bluetooth device
908 */
909 public BluetoothDevice getDevice() {
910 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800911 }
912
913 /**
914 * Discovers services offered by a remote device as well as their
915 * characteristics and descriptors.
916 *
917 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700918 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800919 * triggered. If the discovery was successful, the remote services can be
920 * retrieved using the {@link #getServices} function.
921 *
922 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
923 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800924 * @return true, if the remote service discovery has been started
925 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800926 public boolean discoverServices() {
927 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800928 if (mService == null || mClientIf == 0) return false;
929
930 mServices.clear();
931
932 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800933 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800934 } catch (RemoteException e) {
935 Log.e(TAG,"",e);
936 return false;
937 }
938
939 return true;
940 }
941
942 /**
Jakub Pawlowskife2bf162017-05-16 10:56:35 -0700943 * Discovers a service by UUID. This is exposed only for passing PTS tests.
944 * It should never be used by real applications. The service is not searched
945 * for characteristics and descriptors, or returned in any callback.
946 *
947 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
948 *
949 * @return true, if the remote service discovery has been started
950 * @hide
951 */
952 public boolean discoverServiceByUuid(UUID uuid) {
953 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
954 if (mService == null || mClientIf == 0) return false;
955
956 mServices.clear();
957
958 try {
959 mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
960 } catch (RemoteException e) {
961 Log.e(TAG, "", e);
962 return false;
963 }
964 return true;
965 }
966
967 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800968 * Returns a list of GATT services offered by the remote device.
969 *
970 * <p>This function requires that service discovery has been completed
971 * for the given device.
972 *
973 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
974 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800975 * @return List of services on the remote device. Returns an empty list
976 * if service discovery has not yet been performed.
977 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800978 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800979 List<BluetoothGattService> result =
980 new ArrayList<BluetoothGattService>();
981
982 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800983 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800984 result.add(service);
985 }
986 }
987
988 return result;
989 }
990
991 /**
992 * Returns a {@link BluetoothGattService}, if the requested UUID is
993 * supported by the remote device.
994 *
995 * <p>This function requires that service discovery has been completed
996 * for the given device.
997 *
998 * <p>If multiple instances of the same service (as identified by UUID)
999 * exist, the first instance of the service is returned.
1000 *
1001 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1002 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001003 * @param uuid UUID of the requested service
1004 * @return BluetoothGattService if supported, or null if the requested
1005 * service is not offered by the remote device.
1006 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001007 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001008 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001009 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001010 service.getUuid().equals(uuid)) {
1011 return service;
1012 }
1013 }
1014
1015 return null;
1016 }
1017
1018 /**
1019 * Reads the requested characteristic from the associated remote device.
1020 *
1021 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001022 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001023 * callback.
1024 *
1025 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1026 *
1027 * @param characteristic Characteristic to read from the remote device
1028 * @return true, if the read operation was initiated successfully
1029 */
1030 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
1031 if ((characteristic.getProperties() &
1032 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
1033
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001034 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001035 if (mService == null || mClientIf == 0) return false;
1036
1037 BluetoothGattService service = characteristic.getService();
1038 if (service == null) return false;
1039
1040 BluetoothDevice device = service.getDevice();
1041 if (device == null) return false;
1042
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001043 synchronized(mDeviceBusy) {
1044 if (mDeviceBusy) return false;
1045 mDeviceBusy = true;
1046 }
1047
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001048 try {
1049 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001050 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001051 } catch (RemoteException e) {
1052 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001053 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001054 return false;
1055 }
1056
1057 return true;
1058 }
1059
1060 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001061 * Reads the characteristic using its UUID from the associated remote device.
1062 *
1063 * <p>This is an asynchronous operation. The result of the read operation
1064 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1065 * callback.
1066 *
1067 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1068 *
1069 * @param uuid UUID of characteristic to read from the remote device
1070 * @return true, if the read operation was initiated successfully
1071 * @hide
1072 */
1073 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1074 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1075 if (mService == null || mClientIf == 0) return false;
1076
1077 synchronized(mDeviceBusy) {
1078 if (mDeviceBusy) return false;
1079 mDeviceBusy = true;
1080 }
1081
1082 try {
1083 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
1084 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
1085 } catch (RemoteException e) {
1086 Log.e(TAG,"",e);
1087 mDeviceBusy = false;
1088 return false;
1089 }
1090
1091 return true;
1092 }
1093
1094
1095 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001096 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001097 *
1098 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001099 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001100 * reporting the result of the operation.
1101 *
1102 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1103 *
1104 * @param characteristic Characteristic to write on the remote device
1105 * @return true, if the write operation was initiated successfully
1106 */
1107 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1108 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
1109 && (characteristic.getProperties() &
1110 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
1111
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001112 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001113 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001114
1115 BluetoothGattService service = characteristic.getService();
1116 if (service == null) return false;
1117
1118 BluetoothDevice device = service.getDevice();
1119 if (device == null) return false;
1120
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001121 synchronized(mDeviceBusy) {
1122 if (mDeviceBusy) return false;
1123 mDeviceBusy = true;
1124 }
1125
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001126 try {
1127 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001128 characteristic.getInstanceId(), characteristic.getWriteType(),
1129 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001130 } catch (RemoteException e) {
1131 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001132 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001133 return false;
1134 }
1135
1136 return true;
1137 }
1138
1139 /**
1140 * Reads the value for a given descriptor from the associated remote device.
1141 *
1142 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001143 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001144 * triggered, signaling the result of the operation.
1145 *
1146 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1147 *
1148 * @param descriptor Descriptor value to read from the remote device
1149 * @return true, if the read operation was initiated successfully
1150 */
1151 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001152 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001153 if (mService == null || mClientIf == 0) return false;
1154
1155 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1156 if (characteristic == null) return false;
1157
1158 BluetoothGattService service = characteristic.getService();
1159 if (service == null) return false;
1160
1161 BluetoothDevice device = service.getDevice();
1162 if (device == null) return false;
1163
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001164 synchronized(mDeviceBusy) {
1165 if (mDeviceBusy) return false;
1166 mDeviceBusy = true;
1167 }
1168
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001169 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001170 mService.readDescriptor(mClientIf, device.getAddress(),
1171 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001172 } catch (RemoteException e) {
1173 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001174 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001175 return false;
1176 }
1177
1178 return true;
1179 }
1180
1181 /**
1182 * Write the value of a given descriptor to the associated remote device.
1183 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001184 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001185 * triggered to report the result of the write operation.
1186 *
1187 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1188 *
1189 * @param descriptor Descriptor to write to the associated remote device
1190 * @return true, if the write operation was initiated successfully
1191 */
1192 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001193 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001194 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001195
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
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001205 synchronized(mDeviceBusy) {
1206 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.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jakub Pawlowski8e970d62016-03-30 22:58:17 -07001212 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001213 } catch (RemoteException e) {
1214 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 * Initiates a reliable write transaction for a given remote device.
1224 *
1225 * <p>Once a reliable write transaction has been initiated, all calls
1226 * to {@link #writeCharacteristic} are sent to the remote device for
1227 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001228 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001229 * in response to every {@link #writeCharacteristic} call and is responsible
1230 * for verifying if the value has been transmitted accurately.
1231 *
1232 * <p>After all characteristics have been queued up and verified,
1233 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1234 * was not written correctly, calling {@link #abortReliableWrite} will
1235 * cancel the current transaction without commiting any values on the
1236 * remote device.
1237 *
1238 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1239 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001240 * @return true, if the reliable write transaction has been initiated
1241 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001242 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001243 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001244 if (mService == null || mClientIf == 0) return false;
1245
1246 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001247 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001248 } catch (RemoteException e) {
1249 Log.e(TAG,"",e);
1250 return false;
1251 }
1252
1253 return true;
1254 }
1255
1256 /**
1257 * Executes a reliable write transaction for a given remote device.
1258 *
1259 * <p>This function will commit all queued up characteristic write
1260 * operations for a given remote device.
1261 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001262 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001263 * invoked to indicate whether the transaction has been executed correctly.
1264 *
1265 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1266 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001267 * @return true, if the request to execute the transaction has been sent
1268 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001269 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001270 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001271 if (mService == null || mClientIf == 0) return false;
1272
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001273 synchronized(mDeviceBusy) {
1274 if (mDeviceBusy) return false;
1275 mDeviceBusy = true;
1276 }
1277
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001278 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001279 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001280 } catch (RemoteException e) {
1281 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001282 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001283 return false;
1284 }
1285
1286 return true;
1287 }
1288
1289 /**
1290 * Cancels a reliable write transaction for a given device.
1291 *
1292 * <p>Calling this function will discard all queued characteristic write
1293 * operations for a given remote device.
1294 *
1295 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001296 */
John Du48f8b5d2013-08-19 12:20:37 -07001297 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001298 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001299 if (mService == null || mClientIf == 0) return;
1300
1301 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001302 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001303 } catch (RemoteException e) {
1304 Log.e(TAG,"",e);
1305 }
1306 }
1307
1308 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001309 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001310 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001311 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001312 public void abortReliableWrite(BluetoothDevice mDevice) {
1313 abortReliableWrite();
1314 }
1315
1316 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001317 * Enable or disable notifications/indications for a given characteristic.
1318 *
1319 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001320 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001321 * triggered if the remote device indicates that the given characteristic
1322 * has changed.
1323 *
1324 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1325 *
1326 * @param characteristic The characteristic for which to enable notifications
1327 * @param enable Set to true to enable notifications/indications
1328 * @return true, if the requested notification status was set successfully
1329 */
1330 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1331 boolean enable) {
1332 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1333 + " enable: " + enable);
1334 if (mService == null || mClientIf == 0) return false;
1335
1336 BluetoothGattService service = characteristic.getService();
1337 if (service == null) return false;
1338
1339 BluetoothDevice device = service.getDevice();
1340 if (device == null) return false;
1341
1342 try {
1343 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001344 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001345 } catch (RemoteException e) {
1346 Log.e(TAG,"",e);
1347 return false;
1348 }
1349
1350 return true;
1351 }
1352
1353 /**
1354 * Clears the internal cache and forces a refresh of the services from the
1355 * remote device.
1356 * @hide
1357 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001358 public boolean refresh() {
1359 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001360 if (mService == null || mClientIf == 0) return false;
1361
1362 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001363 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001364 } catch (RemoteException e) {
1365 Log.e(TAG,"",e);
1366 return false;
1367 }
1368
1369 return true;
1370 }
1371
1372 /**
1373 * Read the RSSI for a connected remote device.
1374 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001375 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001376 * invoked when the RSSI value has been read.
1377 *
1378 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1379 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001380 * @return true, if the RSSI value has been requested successfully
1381 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001382 public boolean readRemoteRssi() {
1383 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001384 if (mService == null || mClientIf == 0) return false;
1385
1386 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001387 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001388 } catch (RemoteException e) {
1389 Log.e(TAG,"",e);
1390 return false;
1391 }
1392
1393 return true;
1394 }
1395
1396 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001397 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001398 *
1399 * <p>When performing a write request operation (write without response),
1400 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001401 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001402 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001403 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001404 * whether this operation was successful.
1405 *
1406 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1407 *
1408 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001409 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001410 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001411 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1412 + " mtu: " + mtu);
1413 if (mService == null || mClientIf == 0) return false;
1414
1415 try {
1416 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1417 } catch (RemoteException e) {
1418 Log.e(TAG,"",e);
1419 return false;
1420 }
1421
1422 return true;
1423 }
1424
1425 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001426 * Request a connection parameter update.
1427 *
1428 * <p>This function will send a connection parameter update request to the
1429 * remote device.
1430 *
1431 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001432 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1433 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1434 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001435 * @throws IllegalArgumentException If the parameters are outside of their
1436 * specified range.
1437 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001438 public boolean requestConnectionPriority(int connectionPriority) {
1439 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1440 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001441 throw new IllegalArgumentException("connectionPriority not within valid range");
1442 }
1443
Andre Eisenbach4072da02014-08-19 17:58:55 -07001444 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001445 if (mService == null || mClientIf == 0) return false;
1446
1447 try {
1448 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1449 } catch (RemoteException e) {
1450 Log.e(TAG,"",e);
1451 return false;
1452 }
1453
1454 return true;
1455 }
1456
1457 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001458 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1459 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001460 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001461 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001462 */
1463 @Override
1464 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001465 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001466 }
1467
1468 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001469 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1470 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001471 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001472 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001473 */
1474 @Override
1475 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001476 throw new UnsupportedOperationException
1477 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001478 }
1479
1480 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001481 * Not supported - please use
1482 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1483 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001484 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001485 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001486 */
1487 @Override
1488 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001489 throw new UnsupportedOperationException
1490 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001491 }
1492}