blob: ea2dca08fd4775ad291099e6739796ccff5cefcb [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
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080019import android.content.Context;
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 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080034 * <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
Matthew Xieddf7e472013-03-01 18:41:02 -080044 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045 private IBluetoothGatt mService;
46 private BluetoothGattCallback mCallback;
47 private int mClientIf;
48 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080049 private BluetoothDevice mDevice;
50 private boolean mAutoConnect;
51 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;
Matthew Xieddf7e472013-03-01 18:41:02 -080055
56 private static final int CONN_STATE_IDLE = 0;
57 private static final int CONN_STATE_CONNECTING = 1;
58 private static final int CONN_STATE_CONNECTED = 2;
59 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070060 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080061
62 private List<BluetoothGattService> mServices;
63
Matthew Xieddf7e472013-03-01 18:41:02 -080064 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065 public static final int GATT_SUCCESS = 0;
66
Matthew Xieddf7e472013-03-01 18:41:02 -080067 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080068 public static final int GATT_READ_NOT_PERMITTED = 0x2;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
72
73 /** Insufficient authentication for a given operation */
74 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
75
76 /** The given request is not supported */
77 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
78
79 /** Insufficient encryption for a given operation */
80 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
81
82 /** A read or write operation was requested with an invalid offset */
83 public static final int GATT_INVALID_OFFSET = 0x7;
84
85 /** A write operation exceeds the maximum length of the attribute */
86 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
87
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070088 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070089 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
90
Matthew Xie90ca8072013-05-28 21:06:50 +000091 /** A GATT operation failed, errors other than the above */
92 public static final int GATT_FAILURE = 0x101;
93
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080094 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070095 * Connection paramter update - Use the connection paramters recommended by the
96 * Bluetooth SIG. This is the default value if no connection parameter update
97 * is requested.
98 */
Andre Eisenbach4072da02014-08-19 17:58:55 -070099 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700100
101 /**
102 * Connection paramter update - Request a high priority, low latency connection.
103 * An application should only request high priority connection paramters to transfer
104 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700105 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700106 * to reduce energy use.
107 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700108 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700109
110 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700111 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112
113 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800114 * No authentication required.
115 * @hide
116 */
117 /*package*/ static final int AUTHENTICATION_NONE = 0;
118
119 /**
120 * Authentication requested; no man-in-the-middle protection required.
121 * @hide
122 */
123 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
124
125 /**
126 * Authentication with man-in-the-middle protection requested.
127 * @hide
128 */
129 /*package*/ static final int AUTHENTICATION_MITM = 2;
130
131 /**
Wei Wange0d4afb2014-07-29 21:34:25 -0700132 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800133 */
134 private final IBluetoothGattCallback mBluetoothGattCallback =
Wei Wange0d4afb2014-07-29 21:34:25 -0700135 new BluetoothGattCallbackWrapper() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800136 /**
137 * Application interface registered - app is ready to go
138 * @hide
139 */
140 public void onClientRegistered(int status, int clientIf) {
141 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
142 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800143 if (VDBG) {
144 synchronized(mStateLock) {
145 if (mConnState != CONN_STATE_CONNECTING) {
146 Log.e(TAG, "Bad connection state: " + mConnState);
147 }
148 }
149 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800150 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800151 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700152 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800153 BluetoothProfile.STATE_DISCONNECTED);
154 synchronized(mStateLock) {
155 mConnState = CONN_STATE_IDLE;
156 }
157 return;
158 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800159 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800160 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700161 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800162 } catch (RemoteException e) {
163 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800164 }
165 }
166
167 /**
168 * Client connection state changed
169 * @hide
170 */
171 public void onClientConnectionState(int status, int clientIf,
172 boolean connected, String address) {
173 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
174 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800175 if (!address.equals(mDevice.getAddress())) {
176 return;
177 }
178 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
179 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800180 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700181 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800182 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700183 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800184 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800185
186 synchronized(mStateLock) {
187 if (connected) {
188 mConnState = CONN_STATE_CONNECTED;
189 } else {
190 mConnState = CONN_STATE_IDLE;
191 }
192 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700193
194 synchronized(mDeviceBusy) {
195 mDeviceBusy = false;
196 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800197 }
198
199 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800200 * A new GATT service has been discovered.
201 * The service is added to the internal list and the search
202 * continues.
203 * @hide
204 */
205 public void onGetService(String address, int srvcType,
206 int srvcInstId, ParcelUuid srvcUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700207 if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800208 if (!address.equals(mDevice.getAddress())) {
209 return;
210 }
211 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800212 srvcInstId, srvcType));
213 }
214
215 /**
216 * An included service has been found durig GATT discovery.
217 * The included service is added to the respective parent.
218 * @hide
219 */
220 public void onGetIncludedService(String address, int srvcType,
221 int srvcInstId, ParcelUuid srvcUuid,
222 int inclSrvcType, int inclSrvcInstId,
223 ParcelUuid inclSrvcUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700224 if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800225 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
226
Matthew Xieddf7e472013-03-01 18:41:02 -0800227 if (!address.equals(mDevice.getAddress())) {
228 return;
229 }
230 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800231 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800232 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800233 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
234
235 if (service != null && includedService != null) {
236 service.addIncludedService(includedService);
237 }
238 }
239
240 /**
241 * A new GATT characteristic has been discovered.
242 * Add the new characteristic to the relevant service and continue
243 * the remote device inspection.
244 * @hide
245 */
246 public void onGetCharacteristic(String address, int srvcType,
247 int srvcInstId, ParcelUuid srvcUuid,
248 int charInstId, ParcelUuid charUuid,
249 int charProps) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700250 if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800251 charUuid);
252
Matthew Xieddf7e472013-03-01 18:41:02 -0800253 if (!address.equals(mDevice.getAddress())) {
254 return;
255 }
256 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800257 srvcInstId, srvcType);
258 if (service != null) {
259 service.addCharacteristic(new BluetoothGattCharacteristic(
260 service, charUuid.getUuid(), charInstId, charProps, 0));
261 }
262 }
263
264 /**
265 * A new GATT descriptor has been discovered.
266 * Finally, add the descriptor to the related characteristic.
267 * This should conclude the remote device update.
268 * @hide
269 */
270 public void onGetDescriptor(String address, int srvcType,
271 int srvcInstId, ParcelUuid srvcUuid,
272 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700273 int descrInstId, ParcelUuid descUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700274 if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800275
Matthew Xieddf7e472013-03-01 18:41:02 -0800276 if (!address.equals(mDevice.getAddress())) {
277 return;
278 }
279 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800280 srvcInstId, srvcType);
281 if (service == null) return;
282
283 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
Mike J. Chen2975c682014-06-24 10:19:45 -0700284 charUuid.getUuid(), charInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800285 if (characteristic == null) return;
286
287 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700288 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800289 }
290
291 /**
292 * Remote search has been completed.
293 * The internal object structure should now reflect the state
294 * of the remote device database. Let the application know that
295 * we are done at this point.
296 * @hide
297 */
298 public void onSearchComplete(String address, int status) {
299 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800300 if (!address.equals(mDevice.getAddress())) {
301 return;
302 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800303 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700304 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800305 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700306 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800307 }
308 }
309
310 /**
311 * Remote characteristic has been read.
312 * Updates the internal value.
313 * @hide
314 */
315 public void onCharacteristicRead(String address, int status, int srvcType,
316 int srvcInstId, ParcelUuid srvcUuid,
317 int charInstId, ParcelUuid charUuid, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700318 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800319 + " UUID=" + charUuid + " Status=" + status);
320
Matthew Xieddf7e472013-03-01 18:41:02 -0800321 if (!address.equals(mDevice.getAddress())) {
322 return;
323 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700324
325 synchronized(mDeviceBusy) {
326 mDeviceBusy = false;
327 }
328
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800329 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
330 || status == GATT_INSUFFICIENT_ENCRYPTION)
331 && mAuthRetry == false) {
332 try {
333 mAuthRetry = true;
334 mService.readCharacteristic(mClientIf, address,
335 srvcType, srvcInstId, srvcUuid,
336 charInstId, charUuid, AUTHENTICATION_MITM);
337 return;
338 } catch (RemoteException e) {
339 Log.e(TAG,"",e);
340 }
341 }
342
343 mAuthRetry = false;
344
Matthew Xieddf7e472013-03-01 18:41:02 -0800345 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346 srvcInstId, srvcType);
347 if (service == null) return;
348
349 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
350 charUuid.getUuid(), charInstId);
351 if (characteristic == null) return;
352
353 if (status == 0) characteristic.setValue(value);
354
355 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700356 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700358 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800359 }
360 }
361
362 /**
363 * Characteristic has been written to the remote device.
364 * Let the app know how we did...
365 * @hide
366 */
367 public void onCharacteristicWrite(String address, int status, int srvcType,
368 int srvcInstId, ParcelUuid srvcUuid,
369 int charInstId, ParcelUuid charUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700370 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800371 + " UUID=" + charUuid + " Status=" + status);
372
Matthew Xieddf7e472013-03-01 18:41:02 -0800373 if (!address.equals(mDevice.getAddress())) {
374 return;
375 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700376
377 synchronized(mDeviceBusy) {
378 mDeviceBusy = false;
379 }
380
Matthew Xieddf7e472013-03-01 18:41:02 -0800381 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800382 srvcInstId, srvcType);
383 if (service == null) return;
384
385 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
386 charUuid.getUuid(), charInstId);
387 if (characteristic == null) return;
388
389 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
390 || status == GATT_INSUFFICIENT_ENCRYPTION)
391 && mAuthRetry == false) {
392 try {
393 mAuthRetry = true;
394 mService.writeCharacteristic(mClientIf, address,
395 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
396 characteristic.getWriteType(), AUTHENTICATION_MITM,
397 characteristic.getValue());
398 return;
399 } catch (RemoteException e) {
400 Log.e(TAG,"",e);
401 }
402 }
403
404 mAuthRetry = false;
405
406 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700407 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800408 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700409 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800410 }
411 }
412
413 /**
414 * Remote characteristic has been updated.
415 * Updates the internal value.
416 * @hide
417 */
418 public void onNotify(String address, int srvcType,
419 int srvcInstId, ParcelUuid srvcUuid,
420 int charInstId, ParcelUuid charUuid,
421 byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700422 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800423
Matthew Xieddf7e472013-03-01 18:41:02 -0800424 if (!address.equals(mDevice.getAddress())) {
425 return;
426 }
427 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800428 srvcInstId, srvcType);
429 if (service == null) return;
430
431 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
432 charUuid.getUuid(), charInstId);
433 if (characteristic == null) return;
434
435 characteristic.setValue(value);
436
437 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700438 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800439 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700440 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800441 }
442 }
443
444 /**
445 * Descriptor has been read.
446 * @hide
447 */
448 public void onDescriptorRead(String address, int status, int srvcType,
449 int srvcInstId, ParcelUuid srvcUuid,
450 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700451 int descrInstId, ParcelUuid descrUuid,
452 byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700453 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800454
Matthew Xieddf7e472013-03-01 18:41:02 -0800455 if (!address.equals(mDevice.getAddress())) {
456 return;
457 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700458
459 synchronized(mDeviceBusy) {
460 mDeviceBusy = false;
461 }
462
Matthew Xieddf7e472013-03-01 18:41:02 -0800463 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800464 srvcInstId, srvcType);
465 if (service == null) return;
466
467 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
468 charUuid.getUuid(), charInstId);
469 if (characteristic == null) return;
470
471 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700472 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800473 if (descriptor == null) return;
474
475 if (status == 0) descriptor.setValue(value);
476
477 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
478 || status == GATT_INSUFFICIENT_ENCRYPTION)
479 && mAuthRetry == false) {
480 try {
481 mAuthRetry = true;
482 mService.readDescriptor(mClientIf, address,
483 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700484 descrInstId, descrUuid, AUTHENTICATION_MITM);
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700485 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800486 } catch (RemoteException e) {
487 Log.e(TAG,"",e);
488 }
489 }
490
491 mAuthRetry = true;
492
493 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700494 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800495 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700496 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800497 }
498 }
499
500 /**
501 * Descriptor write operation complete.
502 * @hide
503 */
504 public void onDescriptorWrite(String address, int status, int srvcType,
505 int srvcInstId, ParcelUuid srvcUuid,
506 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700507 int descrInstId, ParcelUuid descrUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700508 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800509
Matthew Xieddf7e472013-03-01 18:41:02 -0800510 if (!address.equals(mDevice.getAddress())) {
511 return;
512 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700513
514 synchronized(mDeviceBusy) {
515 mDeviceBusy = false;
516 }
517
Matthew Xieddf7e472013-03-01 18:41:02 -0800518 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 srvcInstId, srvcType);
520 if (service == null) return;
521
522 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
523 charUuid.getUuid(), charInstId);
524 if (characteristic == null) return;
525
526 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700527 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800528 if (descriptor == null) return;
529
530 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
531 || status == GATT_INSUFFICIENT_ENCRYPTION)
532 && mAuthRetry == false) {
533 try {
534 mAuthRetry = true;
535 mService.writeDescriptor(mClientIf, address,
536 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700537 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800538 AUTHENTICATION_MITM, descriptor.getValue());
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700539 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800540 } catch (RemoteException e) {
541 Log.e(TAG,"",e);
542 }
543 }
544
545 mAuthRetry = false;
546
547 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700548 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800549 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700550 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800551 }
552 }
553
554 /**
555 * Prepared write transaction completed (or aborted)
556 * @hide
557 */
558 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700559 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800560 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800561 if (!address.equals(mDevice.getAddress())) {
562 return;
563 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700564
565 synchronized(mDeviceBusy) {
566 mDeviceBusy = false;
567 }
568
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800569 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700570 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800571 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700572 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800573 }
574 }
575
576 /**
577 * Remote device RSSI has been read
578 * @hide
579 */
580 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700581 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800583 if (!address.equals(mDevice.getAddress())) {
584 return;
585 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800586 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700587 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800588 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700589 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800590 }
591 }
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 */
597 public void onConfigureMTU(String address, int mtu, int status) {
598 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
599 " mtu=" + mtu + " status=" + status);
600 if (!address.equals(mDevice.getAddress())) {
601 return;
602 }
603 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700604 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700605 } catch (Exception ex) {
606 Log.w(TAG, "Unhandled exception in callback", ex);
607 }
Wei Wangf3055892014-03-11 22:22:41 -0700608 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800609 };
610
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700611 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
612 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800613 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800614 mService = iGatt;
615 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700616 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800617 mServices = new ArrayList<BluetoothGattService>();
618
Matthew Xieddf7e472013-03-01 18:41:02 -0800619 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800620 }
621
622 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700623 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700624 *
625 * Application should call this method as early as possible after it is done with
626 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800627 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700628 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800629 if (DBG) Log.d(TAG, "close()");
630
631 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700632 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800633 }
634
635 /**
636 * Returns a service by UUID, instance and type.
637 * @hide
638 */
639 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
640 int instanceId, int type) {
641 for(BluetoothGattService svc : mServices) {
642 if (svc.getDevice().equals(device) &&
643 svc.getType() == type &&
644 svc.getInstanceId() == instanceId &&
645 svc.getUuid().equals(uuid)) {
646 return svc;
647 }
648 }
649 return null;
650 }
651
652
653 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800654 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800655 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800656 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
657 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800658 *
659 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
660 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800661 * @param callback GATT callback handler that will receive asynchronous callbacks.
662 * @return If true, the callback will be called to notify success or failure,
663 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800664 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800665 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800666 if (DBG) Log.d(TAG, "registerApp()");
667 if (mService == null) return false;
668
669 mCallback = callback;
670 UUID uuid = UUID.randomUUID();
671 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
672
673 try {
674 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
675 } catch (RemoteException e) {
676 Log.e(TAG,"",e);
677 return false;
678 }
679
680 return true;
681 }
682
683 /**
684 * Unregister the current application and callbacks.
685 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800686 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800687 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
688 if (mService == null || mClientIf == 0) return;
689
690 try {
691 mCallback = null;
692 mService.unregisterClient(mClientIf);
693 mClientIf = 0;
694 } catch (RemoteException e) {
695 Log.e(TAG,"",e);
696 }
697 }
698
699 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800700 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800701 *
702 * <p>The connection may not be established right away, but will be
703 * completed when the remote device is available. A
704 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
705 * invoked when the connection state changes as a result of this function.
706 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700707 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800708 * the remote device, or rather passively scan and finalize the connection
709 * when the remote device is in range/available. Generally, the first ever
710 * connection to a device should be direct (autoConnect set to false) and
711 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800712 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800713 *
714 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
715 *
716 * @param device Remote device to connect to
717 * @param autoConnect Whether to directly connect to the remote device (false)
718 * or to automatically connect as soon as the remote
719 * device becomes available (true).
720 * @return true, if the connection attempt was initiated successfully
721 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800722 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
723 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
724 synchronized(mStateLock) {
725 if (mConnState != CONN_STATE_IDLE) {
726 throw new IllegalStateException("Not idle");
727 }
728 mConnState = CONN_STATE_CONNECTING;
729 }
730 if (!registerApp(callback)) {
731 synchronized(mStateLock) {
732 mConnState = CONN_STATE_IDLE;
733 }
734 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800735 return false;
736 }
737
Matthew Xieddf7e472013-03-01 18:41:02 -0800738 // the connection will continue after successful callback registration
739 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800740 return true;
741 }
742
743 /**
744 * Disconnects an established connection, or cancels a connection attempt
745 * currently in progress.
746 *
747 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800748 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800749 public void disconnect() {
750 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800751 if (mService == null || mClientIf == 0) return;
752
753 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800754 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800755 } catch (RemoteException e) {
756 Log.e(TAG,"",e);
757 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700758 }
759
760 /**
761 * Connect back to remote device.
762 *
763 * <p>This method is used to re-connect to a remote device after the
764 * connection has been dropped. If the device is not in range, the
765 * re-connection will be triggered once the device is back in range.
766 *
767 * @return true, if the connection attempt was initiated successfully
768 */
769 public boolean connect() {
770 try {
771 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700772 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700773 return true;
774 } catch (RemoteException e) {
775 Log.e(TAG,"",e);
776 return false;
777 }
778 }
779
780 /**
781 * Return the remote bluetooth device this GATT client targets to
782 *
783 * @return remote bluetooth device
784 */
785 public BluetoothDevice getDevice() {
786 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800787 }
788
789 /**
790 * Discovers services offered by a remote device as well as their
791 * characteristics and descriptors.
792 *
793 * <p>This is an asynchronous operation. Once service discovery is completed,
794 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
795 * triggered. If the discovery was successful, the remote services can be
796 * retrieved using the {@link #getServices} function.
797 *
798 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
799 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800800 * @return true, if the remote service discovery has been started
801 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800802 public boolean discoverServices() {
803 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800804 if (mService == null || mClientIf == 0) return false;
805
806 mServices.clear();
807
808 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800809 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800810 } catch (RemoteException e) {
811 Log.e(TAG,"",e);
812 return false;
813 }
814
815 return true;
816 }
817
818 /**
819 * Returns a list of GATT services offered by the remote device.
820 *
821 * <p>This function requires that service discovery has been completed
822 * for the given device.
823 *
824 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
825 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800826 * @return List of services on the remote device. Returns an empty list
827 * if service discovery has not yet been performed.
828 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800829 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800830 List<BluetoothGattService> result =
831 new ArrayList<BluetoothGattService>();
832
833 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800834 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800835 result.add(service);
836 }
837 }
838
839 return result;
840 }
841
842 /**
843 * Returns a {@link BluetoothGattService}, if the requested UUID is
844 * supported by the remote device.
845 *
846 * <p>This function requires that service discovery has been completed
847 * for the given device.
848 *
849 * <p>If multiple instances of the same service (as identified by UUID)
850 * exist, the first instance of the service is returned.
851 *
852 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
853 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800854 * @param uuid UUID of the requested service
855 * @return BluetoothGattService if supported, or null if the requested
856 * service is not offered by the remote device.
857 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800858 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800859 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800860 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800861 service.getUuid().equals(uuid)) {
862 return service;
863 }
864 }
865
866 return null;
867 }
868
869 /**
870 * Reads the requested characteristic from the associated remote device.
871 *
872 * <p>This is an asynchronous operation. The result of the read operation
873 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
874 * callback.
875 *
876 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
877 *
878 * @param characteristic Characteristic to read from the remote device
879 * @return true, if the read operation was initiated successfully
880 */
881 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
882 if ((characteristic.getProperties() &
883 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
884
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700885 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800886 if (mService == null || mClientIf == 0) return false;
887
888 BluetoothGattService service = characteristic.getService();
889 if (service == null) return false;
890
891 BluetoothDevice device = service.getDevice();
892 if (device == null) return false;
893
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700894 synchronized(mDeviceBusy) {
895 if (mDeviceBusy) return false;
896 mDeviceBusy = true;
897 }
898
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800899 try {
900 mService.readCharacteristic(mClientIf, device.getAddress(),
901 service.getType(), service.getInstanceId(),
902 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
903 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
904 } catch (RemoteException e) {
905 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700906 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800907 return false;
908 }
909
910 return true;
911 }
912
913 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800914 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800915 *
916 * <p>Once the write operation has been completed, the
917 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
918 * reporting the result of the operation.
919 *
920 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
921 *
922 * @param characteristic Characteristic to write on the remote device
923 * @return true, if the write operation was initiated successfully
924 */
925 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
926 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
927 && (characteristic.getProperties() &
928 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
929
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700930 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800931 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800932
933 BluetoothGattService service = characteristic.getService();
934 if (service == null) return false;
935
936 BluetoothDevice device = service.getDevice();
937 if (device == null) return false;
938
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700939 synchronized(mDeviceBusy) {
940 if (mDeviceBusy) return false;
941 mDeviceBusy = true;
942 }
943
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800944 try {
945 mService.writeCharacteristic(mClientIf, device.getAddress(),
946 service.getType(), service.getInstanceId(),
947 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
948 new ParcelUuid(characteristic.getUuid()),
949 characteristic.getWriteType(), AUTHENTICATION_NONE,
950 characteristic.getValue());
951 } catch (RemoteException e) {
952 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700953 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800954 return false;
955 }
956
957 return true;
958 }
959
960 /**
961 * Reads the value for a given descriptor from the associated remote device.
962 *
963 * <p>Once the read operation has been completed, the
964 * {@link BluetoothGattCallback#onDescriptorRead} callback is
965 * triggered, signaling the result of the operation.
966 *
967 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
968 *
969 * @param descriptor Descriptor value to read from the remote device
970 * @return true, if the read operation was initiated successfully
971 */
972 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700973 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800974 if (mService == null || mClientIf == 0) return false;
975
976 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
977 if (characteristic == null) return false;
978
979 BluetoothGattService service = characteristic.getService();
980 if (service == null) return false;
981
982 BluetoothDevice device = service.getDevice();
983 if (device == null) return false;
984
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700985 synchronized(mDeviceBusy) {
986 if (mDeviceBusy) return false;
987 mDeviceBusy = true;
988 }
989
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800990 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700991 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
992 service.getInstanceId(), new ParcelUuid(service.getUuid()),
993 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
994 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
995 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800996 } catch (RemoteException e) {
997 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700998 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800999 return false;
1000 }
1001
1002 return true;
1003 }
1004
1005 /**
1006 * Write the value of a given descriptor to the associated remote device.
1007 *
1008 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1009 * triggered to report the result of the write operation.
1010 *
1011 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1012 *
1013 * @param descriptor Descriptor to write to the associated remote device
1014 * @return true, if the write operation was initiated successfully
1015 */
1016 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001017 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001018 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001019
1020 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1021 if (characteristic == null) return false;
1022
1023 BluetoothGattService service = characteristic.getService();
1024 if (service == null) return false;
1025
1026 BluetoothDevice device = service.getDevice();
1027 if (device == null) return false;
1028
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001029 synchronized(mDeviceBusy) {
1030 if (mDeviceBusy) return false;
1031 mDeviceBusy = true;
1032 }
1033
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001034 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001035 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1036 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1037 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1038 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001039 characteristic.getWriteType(), AUTHENTICATION_NONE,
1040 descriptor.getValue());
1041 } catch (RemoteException e) {
1042 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001043 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001044 return false;
1045 }
1046
1047 return true;
1048 }
1049
1050 /**
1051 * Initiates a reliable write transaction for a given remote device.
1052 *
1053 * <p>Once a reliable write transaction has been initiated, all calls
1054 * to {@link #writeCharacteristic} are sent to the remote device for
1055 * verification and queued up for atomic execution. The application will
1056 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1057 * in response to every {@link #writeCharacteristic} call and is responsible
1058 * for verifying if the value has been transmitted accurately.
1059 *
1060 * <p>After all characteristics have been queued up and verified,
1061 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1062 * was not written correctly, calling {@link #abortReliableWrite} will
1063 * cancel the current transaction without commiting any values on the
1064 * remote device.
1065 *
1066 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1067 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001068 * @return true, if the reliable write transaction has been initiated
1069 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001070 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001071 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001072 if (mService == null || mClientIf == 0) return false;
1073
1074 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001075 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001076 } catch (RemoteException e) {
1077 Log.e(TAG,"",e);
1078 return false;
1079 }
1080
1081 return true;
1082 }
1083
1084 /**
1085 * Executes a reliable write transaction for a given remote device.
1086 *
1087 * <p>This function will commit all queued up characteristic write
1088 * operations for a given remote device.
1089 *
1090 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1091 * invoked to indicate whether the transaction has been executed correctly.
1092 *
1093 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1094 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001095 * @return true, if the request to execute the transaction has been sent
1096 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001097 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001098 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001099 if (mService == null || mClientIf == 0) return false;
1100
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001101 synchronized(mDeviceBusy) {
1102 if (mDeviceBusy) return false;
1103 mDeviceBusy = true;
1104 }
1105
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001106 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001107 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001108 } catch (RemoteException e) {
1109 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001110 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001111 return false;
1112 }
1113
1114 return true;
1115 }
1116
1117 /**
1118 * Cancels a reliable write transaction for a given device.
1119 *
1120 * <p>Calling this function will discard all queued characteristic write
1121 * operations for a given remote device.
1122 *
1123 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 */
John Du48f8b5d2013-08-19 12:20:37 -07001125 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001126 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001127 if (mService == null || mClientIf == 0) return;
1128
1129 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001130 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001131 } catch (RemoteException e) {
1132 Log.e(TAG,"",e);
1133 }
1134 }
1135
1136 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001137 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001138 */
1139 public void abortReliableWrite(BluetoothDevice mDevice) {
1140 abortReliableWrite();
1141 }
1142
1143 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001144 * Enable or disable notifications/indications for a given characteristic.
1145 *
1146 * <p>Once notifications are enabled for a characteristic, a
1147 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1148 * triggered if the remote device indicates that the given characteristic
1149 * has changed.
1150 *
1151 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1152 *
1153 * @param characteristic The characteristic for which to enable notifications
1154 * @param enable Set to true to enable notifications/indications
1155 * @return true, if the requested notification status was set successfully
1156 */
1157 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1158 boolean enable) {
1159 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1160 + " enable: " + enable);
1161 if (mService == null || mClientIf == 0) return false;
1162
1163 BluetoothGattService service = characteristic.getService();
1164 if (service == null) return false;
1165
1166 BluetoothDevice device = service.getDevice();
1167 if (device == null) return false;
1168
1169 try {
1170 mService.registerForNotification(mClientIf, device.getAddress(),
1171 service.getType(), service.getInstanceId(),
1172 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1173 new ParcelUuid(characteristic.getUuid()),
1174 enable);
1175 } catch (RemoteException e) {
1176 Log.e(TAG,"",e);
1177 return false;
1178 }
1179
1180 return true;
1181 }
1182
1183 /**
1184 * Clears the internal cache and forces a refresh of the services from the
1185 * remote device.
1186 * @hide
1187 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001188 public boolean refresh() {
1189 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001190 if (mService == null || mClientIf == 0) return false;
1191
1192 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001193 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001194 } catch (RemoteException e) {
1195 Log.e(TAG,"",e);
1196 return false;
1197 }
1198
1199 return true;
1200 }
1201
1202 /**
1203 * Read the RSSI for a connected remote device.
1204 *
1205 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1206 * invoked when the RSSI value has been read.
1207 *
1208 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1209 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001210 * @return true, if the RSSI value has been requested successfully
1211 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001212 public boolean readRemoteRssi() {
1213 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001214 if (mService == null || mClientIf == 0) return false;
1215
1216 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001217 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001218 } catch (RemoteException e) {
1219 Log.e(TAG,"",e);
1220 return false;
1221 }
1222
1223 return true;
1224 }
1225
1226 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001227 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001228 *
1229 * <p>When performing a write request operation (write without response),
1230 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001231 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001232 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001233 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001234 * whether this operation was successful.
1235 *
1236 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1237 *
1238 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001239 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001240 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001241 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1242 + " mtu: " + mtu);
1243 if (mService == null || mClientIf == 0) return false;
1244
1245 try {
1246 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1247 } catch (RemoteException e) {
1248 Log.e(TAG,"",e);
1249 return false;
1250 }
1251
1252 return true;
1253 }
1254
1255 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001256 * Request a connection parameter update.
1257 *
1258 * <p>This function will send a connection parameter update request to the
1259 * remote device.
1260 *
1261 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001262 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1263 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1264 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001265 * @throws IllegalArgumentException If the parameters are outside of their
1266 * specified range.
1267 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001268 public boolean requestConnectionPriority(int connectionPriority) {
1269 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1270 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001271 throw new IllegalArgumentException("connectionPriority not within valid range");
1272 }
1273
Andre Eisenbach4072da02014-08-19 17:58:55 -07001274 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001275 if (mService == null || mClientIf == 0) return false;
1276
1277 try {
1278 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1279 } catch (RemoteException e) {
1280 Log.e(TAG,"",e);
1281 return false;
1282 }
1283
1284 return true;
1285 }
1286
1287 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001288 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1289 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001290 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001291 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001292 */
1293 @Override
1294 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001295 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001296 }
1297
1298 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001299 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1300 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001301 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001302 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001303 */
1304 @Override
1305 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001306 throw new UnsupportedOperationException
1307 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001308 }
1309
1310 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001311 * Not supported - please use
1312 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1313 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001314 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001315 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001316 */
1317 @Override
1318 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001319 throw new UnsupportedOperationException
1320 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001321 }
1322}