blob: d77a77ba41274b6fb09548c5c892f6312988cdc9 [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 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700609
610 /**
611 * Callback indicating the remote device connection is congested.
612 * @hide
613 */
614 public void onConnectionCongested(String address, boolean congested) {
615 if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address
616 + " congested=" + congested);
617 if (!address.equals(mDevice.getAddress())) return;
618 try {
619 mCallback.onConnectionCongested(BluetoothGatt.this, congested);
620 } catch (Exception ex) {
621 Log.w(TAG, "Unhandled exception in callback", ex);
622 }
623 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800624 };
625
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700626 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
627 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800628 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800629 mService = iGatt;
630 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700631 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800632 mServices = new ArrayList<BluetoothGattService>();
633
Matthew Xieddf7e472013-03-01 18:41:02 -0800634 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800635 }
636
637 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700638 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700639 *
640 * Application should call this method as early as possible after it is done with
641 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800642 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700643 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800644 if (DBG) Log.d(TAG, "close()");
645
646 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700647 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800648 }
649
650 /**
651 * Returns a service by UUID, instance and type.
652 * @hide
653 */
654 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
655 int instanceId, int type) {
656 for(BluetoothGattService svc : mServices) {
657 if (svc.getDevice().equals(device) &&
658 svc.getType() == type &&
659 svc.getInstanceId() == instanceId &&
660 svc.getUuid().equals(uuid)) {
661 return svc;
662 }
663 }
664 return null;
665 }
666
667
668 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800669 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800670 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800671 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
672 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800673 *
674 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
675 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800676 * @param callback GATT callback handler that will receive asynchronous callbacks.
677 * @return If true, the callback will be called to notify success or failure,
678 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800679 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800680 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800681 if (DBG) Log.d(TAG, "registerApp()");
682 if (mService == null) return false;
683
684 mCallback = callback;
685 UUID uuid = UUID.randomUUID();
686 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
687
688 try {
689 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
690 } catch (RemoteException e) {
691 Log.e(TAG,"",e);
692 return false;
693 }
694
695 return true;
696 }
697
698 /**
699 * Unregister the current application and callbacks.
700 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800701 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
703 if (mService == null || mClientIf == 0) return;
704
705 try {
706 mCallback = null;
707 mService.unregisterClient(mClientIf);
708 mClientIf = 0;
709 } catch (RemoteException e) {
710 Log.e(TAG,"",e);
711 }
712 }
713
714 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800715 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800716 *
717 * <p>The connection may not be established right away, but will be
718 * completed when the remote device is available. A
719 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
720 * invoked when the connection state changes as a result of this function.
721 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700722 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800723 * the remote device, or rather passively scan and finalize the connection
724 * when the remote device is in range/available. Generally, the first ever
725 * connection to a device should be direct (autoConnect set to false) and
726 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800727 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800728 *
729 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
730 *
731 * @param device Remote device to connect to
732 * @param autoConnect Whether to directly connect to the remote device (false)
733 * or to automatically connect as soon as the remote
734 * device becomes available (true).
735 * @return true, if the connection attempt was initiated successfully
736 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800737 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
738 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
739 synchronized(mStateLock) {
740 if (mConnState != CONN_STATE_IDLE) {
741 throw new IllegalStateException("Not idle");
742 }
743 mConnState = CONN_STATE_CONNECTING;
744 }
745 if (!registerApp(callback)) {
746 synchronized(mStateLock) {
747 mConnState = CONN_STATE_IDLE;
748 }
749 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800750 return false;
751 }
752
Matthew Xieddf7e472013-03-01 18:41:02 -0800753 // the connection will continue after successful callback registration
754 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800755 return true;
756 }
757
758 /**
759 * Disconnects an established connection, or cancels a connection attempt
760 * currently in progress.
761 *
762 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800763 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800764 public void disconnect() {
765 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800766 if (mService == null || mClientIf == 0) return;
767
768 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800769 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800770 } catch (RemoteException e) {
771 Log.e(TAG,"",e);
772 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700773 }
774
775 /**
776 * Connect back to remote device.
777 *
778 * <p>This method is used to re-connect to a remote device after the
779 * connection has been dropped. If the device is not in range, the
780 * re-connection will be triggered once the device is back in range.
781 *
782 * @return true, if the connection attempt was initiated successfully
783 */
784 public boolean connect() {
785 try {
786 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700787 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700788 return true;
789 } catch (RemoteException e) {
790 Log.e(TAG,"",e);
791 return false;
792 }
793 }
794
795 /**
796 * Return the remote bluetooth device this GATT client targets to
797 *
798 * @return remote bluetooth device
799 */
800 public BluetoothDevice getDevice() {
801 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800802 }
803
804 /**
805 * Discovers services offered by a remote device as well as their
806 * characteristics and descriptors.
807 *
808 * <p>This is an asynchronous operation. Once service discovery is completed,
809 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
810 * triggered. If the discovery was successful, the remote services can be
811 * retrieved using the {@link #getServices} function.
812 *
813 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
814 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800815 * @return true, if the remote service discovery has been started
816 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800817 public boolean discoverServices() {
818 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800819 if (mService == null || mClientIf == 0) return false;
820
821 mServices.clear();
822
823 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800824 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 } catch (RemoteException e) {
826 Log.e(TAG,"",e);
827 return false;
828 }
829
830 return true;
831 }
832
833 /**
834 * Returns a list of GATT services offered by the remote device.
835 *
836 * <p>This function requires that service discovery has been completed
837 * for the given device.
838 *
839 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
840 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800841 * @return List of services on the remote device. Returns an empty list
842 * if service discovery has not yet been performed.
843 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800844 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800845 List<BluetoothGattService> result =
846 new ArrayList<BluetoothGattService>();
847
848 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800849 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800850 result.add(service);
851 }
852 }
853
854 return result;
855 }
856
857 /**
858 * Returns a {@link BluetoothGattService}, if the requested UUID is
859 * supported by the remote device.
860 *
861 * <p>This function requires that service discovery has been completed
862 * for the given device.
863 *
864 * <p>If multiple instances of the same service (as identified by UUID)
865 * exist, the first instance of the service is returned.
866 *
867 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
868 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800869 * @param uuid UUID of the requested service
870 * @return BluetoothGattService if supported, or null if the requested
871 * service is not offered by the remote device.
872 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800873 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800874 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800875 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800876 service.getUuid().equals(uuid)) {
877 return service;
878 }
879 }
880
881 return null;
882 }
883
884 /**
885 * Reads the requested characteristic from the associated remote device.
886 *
887 * <p>This is an asynchronous operation. The result of the read operation
888 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
889 * callback.
890 *
891 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
892 *
893 * @param characteristic Characteristic to read from the remote device
894 * @return true, if the read operation was initiated successfully
895 */
896 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
897 if ((characteristic.getProperties() &
898 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
899
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700900 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800901 if (mService == null || mClientIf == 0) return false;
902
903 BluetoothGattService service = characteristic.getService();
904 if (service == null) return false;
905
906 BluetoothDevice device = service.getDevice();
907 if (device == null) return false;
908
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700909 synchronized(mDeviceBusy) {
910 if (mDeviceBusy) return false;
911 mDeviceBusy = true;
912 }
913
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800914 try {
915 mService.readCharacteristic(mClientIf, device.getAddress(),
916 service.getType(), service.getInstanceId(),
917 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
918 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
919 } catch (RemoteException e) {
920 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700921 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800922 return false;
923 }
924
925 return true;
926 }
927
928 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800929 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800930 *
931 * <p>Once the write operation has been completed, the
932 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
933 * reporting the result of the operation.
934 *
935 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
936 *
937 * @param characteristic Characteristic to write on the remote device
938 * @return true, if the write operation was initiated successfully
939 */
940 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
941 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
942 && (characteristic.getProperties() &
943 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
944
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700945 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800946 if (mService == null || mClientIf == 0) return false;
947
948 BluetoothGattService service = characteristic.getService();
949 if (service == null) return false;
950
951 BluetoothDevice device = service.getDevice();
952 if (device == null) return false;
953
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700954 synchronized(mDeviceBusy) {
955 if (mDeviceBusy) return false;
956 mDeviceBusy = true;
957 }
958
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800959 try {
960 mService.writeCharacteristic(mClientIf, device.getAddress(),
961 service.getType(), service.getInstanceId(),
962 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
963 new ParcelUuid(characteristic.getUuid()),
964 characteristic.getWriteType(), AUTHENTICATION_NONE,
965 characteristic.getValue());
966 } catch (RemoteException e) {
967 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700968 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800969 return false;
970 }
971
972 return true;
973 }
974
975 /**
976 * Reads the value for a given descriptor from the associated remote device.
977 *
978 * <p>Once the read operation has been completed, the
979 * {@link BluetoothGattCallback#onDescriptorRead} callback is
980 * triggered, signaling the result of the operation.
981 *
982 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
983 *
984 * @param descriptor Descriptor value to read from the remote device
985 * @return true, if the read operation was initiated successfully
986 */
987 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700988 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800989 if (mService == null || mClientIf == 0) return false;
990
991 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
992 if (characteristic == null) return false;
993
994 BluetoothGattService service = characteristic.getService();
995 if (service == null) return false;
996
997 BluetoothDevice device = service.getDevice();
998 if (device == null) return false;
999
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001000 synchronized(mDeviceBusy) {
1001 if (mDeviceBusy) return false;
1002 mDeviceBusy = true;
1003 }
1004
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001005 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001006 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
1007 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1008 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1009 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
1010 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001011 } catch (RemoteException e) {
1012 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001013 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001014 return false;
1015 }
1016
1017 return true;
1018 }
1019
1020 /**
1021 * Write the value of a given descriptor to the associated remote device.
1022 *
1023 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1024 * triggered to report the result of the write operation.
1025 *
1026 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1027 *
1028 * @param descriptor Descriptor to write to the associated remote device
1029 * @return true, if the write operation was initiated successfully
1030 */
1031 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001032 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001033 if (mService == null || mClientIf == 0) return false;
1034
1035 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1036 if (characteristic == null) return false;
1037
1038 BluetoothGattService service = characteristic.getService();
1039 if (service == null) return false;
1040
1041 BluetoothDevice device = service.getDevice();
1042 if (device == null) return false;
1043
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001044 synchronized(mDeviceBusy) {
1045 if (mDeviceBusy) return false;
1046 mDeviceBusy = true;
1047 }
1048
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001049 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001050 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1051 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1052 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1053 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001054 characteristic.getWriteType(), AUTHENTICATION_NONE,
1055 descriptor.getValue());
1056 } catch (RemoteException e) {
1057 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001058 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001059 return false;
1060 }
1061
1062 return true;
1063 }
1064
1065 /**
1066 * Initiates a reliable write transaction for a given remote device.
1067 *
1068 * <p>Once a reliable write transaction has been initiated, all calls
1069 * to {@link #writeCharacteristic} are sent to the remote device for
1070 * verification and queued up for atomic execution. The application will
1071 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1072 * in response to every {@link #writeCharacteristic} call and is responsible
1073 * for verifying if the value has been transmitted accurately.
1074 *
1075 * <p>After all characteristics have been queued up and verified,
1076 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1077 * was not written correctly, calling {@link #abortReliableWrite} will
1078 * cancel the current transaction without commiting any values on the
1079 * remote device.
1080 *
1081 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1082 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001083 * @return true, if the reliable write transaction has been initiated
1084 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001085 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001086 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001087 if (mService == null || mClientIf == 0) return false;
1088
1089 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001090 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001091 } catch (RemoteException e) {
1092 Log.e(TAG,"",e);
1093 return false;
1094 }
1095
1096 return true;
1097 }
1098
1099 /**
1100 * Executes a reliable write transaction for a given remote device.
1101 *
1102 * <p>This function will commit all queued up characteristic write
1103 * operations for a given remote device.
1104 *
1105 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1106 * invoked to indicate whether the transaction has been executed correctly.
1107 *
1108 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1109 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001110 * @return true, if the request to execute the transaction has been sent
1111 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001112 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001113 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001114 if (mService == null || mClientIf == 0) return false;
1115
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001116 synchronized(mDeviceBusy) {
1117 if (mDeviceBusy) return false;
1118 mDeviceBusy = true;
1119 }
1120
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001121 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001122 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001123 } catch (RemoteException e) {
1124 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001125 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001126 return false;
1127 }
1128
1129 return true;
1130 }
1131
1132 /**
1133 * Cancels a reliable write transaction for a given device.
1134 *
1135 * <p>Calling this function will discard all queued characteristic write
1136 * operations for a given remote device.
1137 *
1138 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001139 */
John Du48f8b5d2013-08-19 12:20:37 -07001140 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001141 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001142 if (mService == null || mClientIf == 0) return;
1143
1144 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001145 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001146 } catch (RemoteException e) {
1147 Log.e(TAG,"",e);
1148 }
1149 }
1150
1151 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001152 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001153 */
1154 public void abortReliableWrite(BluetoothDevice mDevice) {
1155 abortReliableWrite();
1156 }
1157
1158 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001159 * Enable or disable notifications/indications for a given characteristic.
1160 *
1161 * <p>Once notifications are enabled for a characteristic, a
1162 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1163 * triggered if the remote device indicates that the given characteristic
1164 * has changed.
1165 *
1166 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1167 *
1168 * @param characteristic The characteristic for which to enable notifications
1169 * @param enable Set to true to enable notifications/indications
1170 * @return true, if the requested notification status was set successfully
1171 */
1172 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1173 boolean enable) {
1174 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1175 + " enable: " + enable);
1176 if (mService == null || mClientIf == 0) return false;
1177
1178 BluetoothGattService service = characteristic.getService();
1179 if (service == null) return false;
1180
1181 BluetoothDevice device = service.getDevice();
1182 if (device == null) return false;
1183
1184 try {
1185 mService.registerForNotification(mClientIf, device.getAddress(),
1186 service.getType(), service.getInstanceId(),
1187 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1188 new ParcelUuid(characteristic.getUuid()),
1189 enable);
1190 } catch (RemoteException e) {
1191 Log.e(TAG,"",e);
1192 return false;
1193 }
1194
1195 return true;
1196 }
1197
1198 /**
1199 * Clears the internal cache and forces a refresh of the services from the
1200 * remote device.
1201 * @hide
1202 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001203 public boolean refresh() {
1204 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001205 if (mService == null || mClientIf == 0) return false;
1206
1207 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001208 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001209 } catch (RemoteException e) {
1210 Log.e(TAG,"",e);
1211 return false;
1212 }
1213
1214 return true;
1215 }
1216
1217 /**
1218 * Read the RSSI for a connected remote device.
1219 *
1220 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1221 * invoked when the RSSI value has been read.
1222 *
1223 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1224 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001225 * @return true, if the RSSI value has been requested successfully
1226 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001227 public boolean readRemoteRssi() {
1228 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001229 if (mService == null || mClientIf == 0) return false;
1230
1231 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001232 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 } catch (RemoteException e) {
1234 Log.e(TAG,"",e);
1235 return false;
1236 }
1237
1238 return true;
1239 }
1240
1241 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001242 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001243 *
1244 * <p>When performing a write request operation (write without response),
1245 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001246 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001247 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001248 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001249 * whether this operation was successful.
1250 *
1251 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1252 *
1253 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001254 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001255 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001256 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1257 + " mtu: " + mtu);
1258 if (mService == null || mClientIf == 0) return false;
1259
1260 try {
1261 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1262 } catch (RemoteException e) {
1263 Log.e(TAG,"",e);
1264 return false;
1265 }
1266
1267 return true;
1268 }
1269
1270 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001271 * Request a connection parameter update.
1272 *
1273 * <p>This function will send a connection parameter update request to the
1274 * remote device.
1275 *
1276 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001277 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1278 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1279 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001280 * @throws IllegalArgumentException If the parameters are outside of their
1281 * specified range.
1282 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001283 public boolean requestConnectionPriority(int connectionPriority) {
1284 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1285 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001286 throw new IllegalArgumentException("connectionPriority not within valid range");
1287 }
1288
Andre Eisenbach4072da02014-08-19 17:58:55 -07001289 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001290 if (mService == null || mClientIf == 0) return false;
1291
1292 try {
1293 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1294 } catch (RemoteException e) {
1295 Log.e(TAG,"",e);
1296 return false;
1297 }
1298
1299 return true;
1300 }
1301
1302 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001303 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1304 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001305 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001306 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001307 */
1308 @Override
1309 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001310 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001311 }
1312
1313 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001314 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1315 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001316 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001317 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001318 */
1319 @Override
1320 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001321 throw new UnsupportedOperationException
1322 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001323 }
1324
1325 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001326 * Not supported - please use
1327 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1328 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001329 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001330 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001331 */
1332 @Override
1333 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001334 throw new UnsupportedOperationException
1335 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001336 }
1337}