blob: 8b7b0af9f78317df68b24d596d1cc32743993bf9 [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
Wei Wang9fb17912014-07-01 15:10:06 -070019import android.bluetooth.le.ScanResult;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080020import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080021import android.os.ParcelUuid;
22import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080023import android.util.Log;
24
25import java.util.ArrayList;
26import java.util.List;
27import java.util.UUID;
28
29/**
Matthew Xieddf7e472013-03-01 18:41:02 -080030 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080031 *
Matthew Xieddf7e472013-03-01 18:41:02 -080032 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080033 * with Bluetooth Smart or Smart Ready devices.
34 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080035 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070036 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080037 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
38 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080039 */
40public final class BluetoothGatt implements BluetoothProfile {
41 private static final String TAG = "BluetoothGatt";
42 private static final boolean DBG = true;
Matthew Xieddf7e472013-03-01 18:41:02 -080043 private static final boolean VDBG = true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044
Matthew Xieddf7e472013-03-01 18:41:02 -080045 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080046 private IBluetoothGatt mService;
47 private BluetoothGattCallback mCallback;
48 private int mClientIf;
49 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080050 private BluetoothDevice mDevice;
51 private boolean mAutoConnect;
52 private int mConnState;
53 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070054 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070055 private int mTransport;
Matthew Xieddf7e472013-03-01 18:41:02 -080056
57 private static final int CONN_STATE_IDLE = 0;
58 private static final int CONN_STATE_CONNECTING = 1;
59 private static final int CONN_STATE_CONNECTED = 2;
60 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070061 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080062
63 private List<BluetoothGattService> mServices;
64
Matthew Xieddf7e472013-03-01 18:41:02 -080065 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080066 public static final int GATT_SUCCESS = 0;
67
Matthew Xieddf7e472013-03-01 18:41:02 -080068 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080069 public static final int GATT_READ_NOT_PERMITTED = 0x2;
70
Matthew Xieddf7e472013-03-01 18:41:02 -080071 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080072 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
73
74 /** Insufficient authentication for a given operation */
75 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
76
77 /** The given request is not supported */
78 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
79
80 /** Insufficient encryption for a given operation */
81 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
82
83 /** A read or write operation was requested with an invalid offset */
84 public static final int GATT_INVALID_OFFSET = 0x7;
85
86 /** A write operation exceeds the maximum length of the attribute */
87 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
88
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070089 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070090 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
91
Matthew Xie90ca8072013-05-28 21:06:50 +000092 /** A GATT operation failed, errors other than the above */
93 public static final int GATT_FAILURE = 0x101;
94
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080095 /**
96 * No authentication required.
97 * @hide
98 */
99 /*package*/ static final int AUTHENTICATION_NONE = 0;
100
101 /**
102 * Authentication requested; no man-in-the-middle protection required.
103 * @hide
104 */
105 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
106
107 /**
108 * Authentication with man-in-the-middle protection requested.
109 * @hide
110 */
111 /*package*/ static final int AUTHENTICATION_MITM = 2;
112
113 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800114 * Bluetooth GATT interface callbacks
115 */
116 private final IBluetoothGattCallback mBluetoothGattCallback =
117 new IBluetoothGattCallback.Stub() {
118 /**
119 * Application interface registered - app is ready to go
120 * @hide
121 */
122 public void onClientRegistered(int status, int clientIf) {
123 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
124 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800125 if (VDBG) {
126 synchronized(mStateLock) {
127 if (mConnState != CONN_STATE_CONNECTING) {
128 Log.e(TAG, "Bad connection state: " + mConnState);
129 }
130 }
131 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800132 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800133 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700134 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800135 BluetoothProfile.STATE_DISCONNECTED);
136 synchronized(mStateLock) {
137 mConnState = CONN_STATE_IDLE;
138 }
139 return;
140 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800141 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800142 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700143 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800144 } catch (RemoteException e) {
145 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800146 }
147 }
148
149 /**
150 * Client connection state changed
151 * @hide
152 */
153 public void onClientConnectionState(int status, int clientIf,
154 boolean connected, String address) {
155 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
156 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800157 if (!address.equals(mDevice.getAddress())) {
158 return;
159 }
160 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
161 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800162 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700163 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800164 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700165 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800166 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800167
168 synchronized(mStateLock) {
169 if (connected) {
170 mConnState = CONN_STATE_CONNECTED;
171 } else {
172 mConnState = CONN_STATE_IDLE;
173 }
174 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700175
176 synchronized(mDeviceBusy) {
177 mDeviceBusy = false;
178 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800179 }
180
181 /**
182 * Callback reporting an LE scan result.
183 * @hide
184 */
185 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800186 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800187 }
188
189 /**
190 * A new GATT service has been discovered.
191 * The service is added to the internal list and the search
192 * continues.
193 * @hide
194 */
195 public void onGetService(String address, int srvcType,
196 int srvcInstId, ParcelUuid srvcUuid) {
197 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800198 if (!address.equals(mDevice.getAddress())) {
199 return;
200 }
201 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800202 srvcInstId, srvcType));
203 }
204
205 /**
206 * An included service has been found durig GATT discovery.
207 * The included service is added to the respective parent.
208 * @hide
209 */
210 public void onGetIncludedService(String address, int srvcType,
211 int srvcInstId, ParcelUuid srvcUuid,
212 int inclSrvcType, int inclSrvcInstId,
213 ParcelUuid inclSrvcUuid) {
214 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
215 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
216
Matthew Xieddf7e472013-03-01 18:41:02 -0800217 if (!address.equals(mDevice.getAddress())) {
218 return;
219 }
220 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800221 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800222 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800223 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
224
225 if (service != null && includedService != null) {
226 service.addIncludedService(includedService);
227 }
228 }
229
230 /**
231 * A new GATT characteristic has been discovered.
232 * Add the new characteristic to the relevant service and continue
233 * the remote device inspection.
234 * @hide
235 */
236 public void onGetCharacteristic(String address, int srvcType,
237 int srvcInstId, ParcelUuid srvcUuid,
238 int charInstId, ParcelUuid charUuid,
239 int charProps) {
240 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
241 charUuid);
242
Matthew Xieddf7e472013-03-01 18:41:02 -0800243 if (!address.equals(mDevice.getAddress())) {
244 return;
245 }
246 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800247 srvcInstId, srvcType);
248 if (service != null) {
249 service.addCharacteristic(new BluetoothGattCharacteristic(
250 service, charUuid.getUuid(), charInstId, charProps, 0));
251 }
252 }
253
254 /**
255 * A new GATT descriptor has been discovered.
256 * Finally, add the descriptor to the related characteristic.
257 * This should conclude the remote device update.
258 * @hide
259 */
260 public void onGetDescriptor(String address, int srvcType,
261 int srvcInstId, ParcelUuid srvcUuid,
262 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700263 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800264 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
265
Matthew Xieddf7e472013-03-01 18:41:02 -0800266 if (!address.equals(mDevice.getAddress())) {
267 return;
268 }
269 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800270 srvcInstId, srvcType);
271 if (service == null) return;
272
273 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
Mike J. Chen2975c682014-06-24 10:19:45 -0700274 charUuid.getUuid(), charInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800275 if (characteristic == null) return;
276
277 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700278 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800279 }
280
281 /**
282 * Remote search has been completed.
283 * The internal object structure should now reflect the state
284 * of the remote device database. Let the application know that
285 * we are done at this point.
286 * @hide
287 */
288 public void onSearchComplete(String address, int status) {
289 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800290 if (!address.equals(mDevice.getAddress())) {
291 return;
292 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800293 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700294 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800295 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700296 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800297 }
298 }
299
300 /**
301 * Remote characteristic has been read.
302 * Updates the internal value.
303 * @hide
304 */
305 public void onCharacteristicRead(String address, int status, int srvcType,
306 int srvcInstId, ParcelUuid srvcUuid,
307 int charInstId, ParcelUuid charUuid, byte[] value) {
308 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
309 + " UUID=" + charUuid + " Status=" + status);
310
Matthew Xieddf7e472013-03-01 18:41:02 -0800311 if (!address.equals(mDevice.getAddress())) {
312 return;
313 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700314
315 synchronized(mDeviceBusy) {
316 mDeviceBusy = false;
317 }
318
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800319 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
320 || status == GATT_INSUFFICIENT_ENCRYPTION)
321 && mAuthRetry == false) {
322 try {
323 mAuthRetry = true;
324 mService.readCharacteristic(mClientIf, address,
325 srvcType, srvcInstId, srvcUuid,
326 charInstId, charUuid, AUTHENTICATION_MITM);
327 return;
328 } catch (RemoteException e) {
329 Log.e(TAG,"",e);
330 }
331 }
332
333 mAuthRetry = false;
334
Matthew Xieddf7e472013-03-01 18:41:02 -0800335 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800336 srvcInstId, srvcType);
337 if (service == null) return;
338
339 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
340 charUuid.getUuid(), charInstId);
341 if (characteristic == null) return;
342
343 if (status == 0) characteristic.setValue(value);
344
345 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700346 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800347 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700348 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800349 }
350 }
351
352 /**
353 * Characteristic has been written to the remote device.
354 * Let the app know how we did...
355 * @hide
356 */
357 public void onCharacteristicWrite(String address, int status, int srvcType,
358 int srvcInstId, ParcelUuid srvcUuid,
359 int charInstId, ParcelUuid charUuid) {
360 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
361 + " UUID=" + charUuid + " Status=" + status);
362
Matthew Xieddf7e472013-03-01 18:41:02 -0800363 if (!address.equals(mDevice.getAddress())) {
364 return;
365 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700366
367 synchronized(mDeviceBusy) {
368 mDeviceBusy = false;
369 }
370
Matthew Xieddf7e472013-03-01 18:41:02 -0800371 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800372 srvcInstId, srvcType);
373 if (service == null) return;
374
375 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
376 charUuid.getUuid(), charInstId);
377 if (characteristic == null) return;
378
379 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
380 || status == GATT_INSUFFICIENT_ENCRYPTION)
381 && mAuthRetry == false) {
382 try {
383 mAuthRetry = true;
384 mService.writeCharacteristic(mClientIf, address,
385 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
386 characteristic.getWriteType(), AUTHENTICATION_MITM,
387 characteristic.getValue());
388 return;
389 } catch (RemoteException e) {
390 Log.e(TAG,"",e);
391 }
392 }
393
394 mAuthRetry = false;
395
396 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700397 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800398 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700399 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800400 }
401 }
402
403 /**
404 * Remote characteristic has been updated.
405 * Updates the internal value.
406 * @hide
407 */
408 public void onNotify(String address, int srvcType,
409 int srvcInstId, ParcelUuid srvcUuid,
410 int charInstId, ParcelUuid charUuid,
411 byte[] value) {
412 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
413
Matthew Xieddf7e472013-03-01 18:41:02 -0800414 if (!address.equals(mDevice.getAddress())) {
415 return;
416 }
417 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800418 srvcInstId, srvcType);
419 if (service == null) return;
420
421 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
422 charUuid.getUuid(), charInstId);
423 if (characteristic == null) return;
424
425 characteristic.setValue(value);
426
427 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700428 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800429 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700430 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800431 }
432 }
433
434 /**
435 * Descriptor has been read.
436 * @hide
437 */
438 public void onDescriptorRead(String address, int status, int srvcType,
439 int srvcInstId, ParcelUuid srvcUuid,
440 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700441 int descrInstId, ParcelUuid descrUuid,
442 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800443 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
444
Matthew Xieddf7e472013-03-01 18:41:02 -0800445 if (!address.equals(mDevice.getAddress())) {
446 return;
447 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700448
449 synchronized(mDeviceBusy) {
450 mDeviceBusy = false;
451 }
452
Matthew Xieddf7e472013-03-01 18:41:02 -0800453 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800454 srvcInstId, srvcType);
455 if (service == null) return;
456
457 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
458 charUuid.getUuid(), charInstId);
459 if (characteristic == null) return;
460
461 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700462 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800463 if (descriptor == null) return;
464
465 if (status == 0) descriptor.setValue(value);
466
467 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
468 || status == GATT_INSUFFICIENT_ENCRYPTION)
469 && mAuthRetry == false) {
470 try {
471 mAuthRetry = true;
472 mService.readDescriptor(mClientIf, address,
473 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700474 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800475 } catch (RemoteException e) {
476 Log.e(TAG,"",e);
477 }
478 }
479
480 mAuthRetry = true;
481
482 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700483 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800484 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700485 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800486 }
487 }
488
489 /**
490 * Descriptor write operation complete.
491 * @hide
492 */
493 public void onDescriptorWrite(String address, int status, int srvcType,
494 int srvcInstId, ParcelUuid srvcUuid,
495 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700496 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800497 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
498
Matthew Xieddf7e472013-03-01 18:41:02 -0800499 if (!address.equals(mDevice.getAddress())) {
500 return;
501 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700502
503 synchronized(mDeviceBusy) {
504 mDeviceBusy = false;
505 }
506
Matthew Xieddf7e472013-03-01 18:41:02 -0800507 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800508 srvcInstId, srvcType);
509 if (service == null) return;
510
511 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
512 charUuid.getUuid(), charInstId);
513 if (characteristic == null) return;
514
515 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700516 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800517 if (descriptor == null) return;
518
519 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
520 || status == GATT_INSUFFICIENT_ENCRYPTION)
521 && mAuthRetry == false) {
522 try {
523 mAuthRetry = true;
524 mService.writeDescriptor(mClientIf, address,
525 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700526 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800527 AUTHENTICATION_MITM, descriptor.getValue());
528 } catch (RemoteException e) {
529 Log.e(TAG,"",e);
530 }
531 }
532
533 mAuthRetry = false;
534
535 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700536 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800537 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700538 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800539 }
540 }
541
542 /**
543 * Prepared write transaction completed (or aborted)
544 * @hide
545 */
546 public void onExecuteWrite(String address, int status) {
547 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
548 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800549 if (!address.equals(mDevice.getAddress())) {
550 return;
551 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700552
553 synchronized(mDeviceBusy) {
554 mDeviceBusy = false;
555 }
556
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800557 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700558 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800559 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700560 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800561 }
562 }
563
564 /**
565 * Remote device RSSI has been read
566 * @hide
567 */
568 public void onReadRemoteRssi(String address, int rssi, int status) {
569 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
570 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800571 if (!address.equals(mDevice.getAddress())) {
572 return;
573 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800574 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700575 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800576 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700577 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800578 }
579 }
Wei Wangf3055892014-03-11 22:22:41 -0700580
581 /**
582 * Advertise state change callback
583 * @hide
584 */
585 public void onAdvertiseStateChange(int state, int status) {
586 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
587 + state + " status=" + status);
Wei Wangadf6aff2014-05-20 06:30:20 +0000588 }
589
590 /**
591 * @hide
592 */
593 @Override
594 public void onMultiAdvertiseCallback(int status) {
595 // no op.
596 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700597
598 /**
599 * Callback invoked when the MTU for a given connection changes
600 * @hide
601 */
602 public void onConfigureMTU(String address, int mtu, int status) {
603 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
604 " mtu=" + mtu + " status=" + status);
605 if (!address.equals(mDevice.getAddress())) {
606 return;
607 }
608 try {
609 mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status);
610 } catch (Exception ex) {
611 Log.w(TAG, "Unhandled exception in callback", ex);
612 }
Wei Wangf3055892014-03-11 22:22:41 -0700613 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700614
615 /**
616 * Callback indicating the remote device connection is congested.
617 * @hide
618 */
619 public void onConnectionCongested(String address, boolean congested) {
620 if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address
621 + " congested=" + congested);
622 if (!address.equals(mDevice.getAddress())) return;
623 try {
624 mCallback.onConnectionCongested(BluetoothGatt.this, congested);
625 } catch (Exception ex) {
626 Log.w(TAG, "Unhandled exception in callback", ex);
627 }
628 }
Wei Wang9fb17912014-07-01 15:10:06 -0700629
630 @Override
631 public void onBatchScanResults(List<ScanResult> results) {
632 // no op
633 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800634 };
635
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700636 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
637 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800638 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800639 mService = iGatt;
640 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700641 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800642 mServices = new ArrayList<BluetoothGattService>();
643
Matthew Xieddf7e472013-03-01 18:41:02 -0800644 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800645 }
646
647 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700648 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700649 *
650 * Application should call this method as early as possible after it is done with
651 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800652 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700653 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800654 if (DBG) Log.d(TAG, "close()");
655
656 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700657 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800658 }
659
660 /**
661 * Returns a service by UUID, instance and type.
662 * @hide
663 */
664 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
665 int instanceId, int type) {
666 for(BluetoothGattService svc : mServices) {
667 if (svc.getDevice().equals(device) &&
668 svc.getType() == type &&
669 svc.getInstanceId() == instanceId &&
670 svc.getUuid().equals(uuid)) {
671 return svc;
672 }
673 }
674 return null;
675 }
676
677
678 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800679 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800680 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800681 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
682 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800683 *
684 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
685 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800686 * @param callback GATT callback handler that will receive asynchronous callbacks.
687 * @return If true, the callback will be called to notify success or failure,
688 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800689 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800690 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800691 if (DBG) Log.d(TAG, "registerApp()");
692 if (mService == null) return false;
693
694 mCallback = callback;
695 UUID uuid = UUID.randomUUID();
696 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
697
698 try {
699 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
700 } catch (RemoteException e) {
701 Log.e(TAG,"",e);
702 return false;
703 }
704
705 return true;
706 }
707
708 /**
709 * Unregister the current application and callbacks.
710 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800711 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800712 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
713 if (mService == null || mClientIf == 0) return;
714
715 try {
716 mCallback = null;
717 mService.unregisterClient(mClientIf);
718 mClientIf = 0;
719 } catch (RemoteException e) {
720 Log.e(TAG,"",e);
721 }
722 }
723
724 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800725 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800726 *
727 * <p>The connection may not be established right away, but will be
728 * completed when the remote device is available. A
729 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
730 * invoked when the connection state changes as a result of this function.
731 *
732 * <p>The autoConnect paramter determines whether to actively connect to
733 * the remote device, or rather passively scan and finalize the connection
734 * when the remote device is in range/available. Generally, the first ever
735 * connection to a device should be direct (autoConnect set to false) and
736 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800737 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800738 *
739 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
740 *
741 * @param device Remote device to connect to
742 * @param autoConnect Whether to directly connect to the remote device (false)
743 * or to automatically connect as soon as the remote
744 * device becomes available (true).
745 * @return true, if the connection attempt was initiated successfully
746 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800747 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
748 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
749 synchronized(mStateLock) {
750 if (mConnState != CONN_STATE_IDLE) {
751 throw new IllegalStateException("Not idle");
752 }
753 mConnState = CONN_STATE_CONNECTING;
754 }
755 if (!registerApp(callback)) {
756 synchronized(mStateLock) {
757 mConnState = CONN_STATE_IDLE;
758 }
759 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800760 return false;
761 }
762
Matthew Xieddf7e472013-03-01 18:41:02 -0800763 // the connection will continue after successful callback registration
764 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800765 return true;
766 }
767
768 /**
769 * Disconnects an established connection, or cancels a connection attempt
770 * currently in progress.
771 *
772 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800773 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800774 public void disconnect() {
775 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800776 if (mService == null || mClientIf == 0) return;
777
778 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800779 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800780 } catch (RemoteException e) {
781 Log.e(TAG,"",e);
782 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700783 }
784
785 /**
786 * Connect back to remote device.
787 *
788 * <p>This method is used to re-connect to a remote device after the
789 * connection has been dropped. If the device is not in range, the
790 * re-connection will be triggered once the device is back in range.
791 *
792 * @return true, if the connection attempt was initiated successfully
793 */
794 public boolean connect() {
795 try {
796 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700797 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700798 return true;
799 } catch (RemoteException e) {
800 Log.e(TAG,"",e);
801 return false;
802 }
803 }
804
805 /**
806 * Return the remote bluetooth device this GATT client targets to
807 *
808 * @return remote bluetooth device
809 */
810 public BluetoothDevice getDevice() {
811 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800812 }
813
814 /**
815 * Discovers services offered by a remote device as well as their
816 * characteristics and descriptors.
817 *
818 * <p>This is an asynchronous operation. Once service discovery is completed,
819 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
820 * triggered. If the discovery was successful, the remote services can be
821 * retrieved using the {@link #getServices} function.
822 *
823 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
824 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 * @return true, if the remote service discovery has been started
826 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800827 public boolean discoverServices() {
828 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800829 if (mService == null || mClientIf == 0) return false;
830
831 mServices.clear();
832
833 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800834 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800835 } catch (RemoteException e) {
836 Log.e(TAG,"",e);
837 return false;
838 }
839
840 return true;
841 }
842
843 /**
844 * Returns a list of GATT services offered by the remote device.
845 *
846 * <p>This function requires that service discovery has been completed
847 * for the given device.
848 *
849 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
850 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800851 * @return List of services on the remote device. Returns an empty list
852 * if service discovery has not yet been performed.
853 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800854 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800855 List<BluetoothGattService> result =
856 new ArrayList<BluetoothGattService>();
857
858 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800859 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800860 result.add(service);
861 }
862 }
863
864 return result;
865 }
866
867 /**
868 * Returns a {@link BluetoothGattService}, if the requested UUID is
869 * supported by the remote device.
870 *
871 * <p>This function requires that service discovery has been completed
872 * for the given device.
873 *
874 * <p>If multiple instances of the same service (as identified by UUID)
875 * exist, the first instance of the service is returned.
876 *
877 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
878 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800879 * @param uuid UUID of the requested service
880 * @return BluetoothGattService if supported, or null if the requested
881 * service is not offered by the remote device.
882 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800883 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800884 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800885 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800886 service.getUuid().equals(uuid)) {
887 return service;
888 }
889 }
890
891 return null;
892 }
893
894 /**
895 * Reads the requested characteristic from the associated remote device.
896 *
897 * <p>This is an asynchronous operation. The result of the read operation
898 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
899 * callback.
900 *
901 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
902 *
903 * @param characteristic Characteristic to read from the remote device
904 * @return true, if the read operation was initiated successfully
905 */
906 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
907 if ((characteristic.getProperties() &
908 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
909
910 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
911 if (mService == null || mClientIf == 0) return false;
912
913 BluetoothGattService service = characteristic.getService();
914 if (service == null) return false;
915
916 BluetoothDevice device = service.getDevice();
917 if (device == null) return false;
918
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700919 synchronized(mDeviceBusy) {
920 if (mDeviceBusy) return false;
921 mDeviceBusy = true;
922 }
923
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800924 try {
925 mService.readCharacteristic(mClientIf, device.getAddress(),
926 service.getType(), service.getInstanceId(),
927 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
928 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
929 } catch (RemoteException e) {
930 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700931 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800932 return false;
933 }
934
935 return true;
936 }
937
938 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800939 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800940 *
941 * <p>Once the write operation has been completed, the
942 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
943 * reporting the result of the operation.
944 *
945 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
946 *
947 * @param characteristic Characteristic to write on the remote device
948 * @return true, if the write operation was initiated successfully
949 */
950 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
951 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
952 && (characteristic.getProperties() &
953 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
954
955 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
956 if (mService == null || mClientIf == 0) return false;
957
958 BluetoothGattService service = characteristic.getService();
959 if (service == null) return false;
960
961 BluetoothDevice device = service.getDevice();
962 if (device == null) return false;
963
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700964 synchronized(mDeviceBusy) {
965 if (mDeviceBusy) return false;
966 mDeviceBusy = true;
967 }
968
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800969 try {
970 mService.writeCharacteristic(mClientIf, device.getAddress(),
971 service.getType(), service.getInstanceId(),
972 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
973 new ParcelUuid(characteristic.getUuid()),
974 characteristic.getWriteType(), AUTHENTICATION_NONE,
975 characteristic.getValue());
976 } catch (RemoteException e) {
977 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700978 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800979 return false;
980 }
981
982 return true;
983 }
984
985 /**
986 * Reads the value for a given descriptor from the associated remote device.
987 *
988 * <p>Once the read operation has been completed, the
989 * {@link BluetoothGattCallback#onDescriptorRead} callback is
990 * triggered, signaling the result of the operation.
991 *
992 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
993 *
994 * @param descriptor Descriptor value to read from the remote device
995 * @return true, if the read operation was initiated successfully
996 */
997 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
998 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
999 if (mService == null || mClientIf == 0) return false;
1000
1001 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1002 if (characteristic == null) return false;
1003
1004 BluetoothGattService service = characteristic.getService();
1005 if (service == null) return false;
1006
1007 BluetoothDevice device = service.getDevice();
1008 if (device == null) return false;
1009
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001010 synchronized(mDeviceBusy) {
1011 if (mDeviceBusy) return false;
1012 mDeviceBusy = true;
1013 }
1014
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001016 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
1017 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1018 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1019 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
1020 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001021 } catch (RemoteException e) {
1022 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001023 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001024 return false;
1025 }
1026
1027 return true;
1028 }
1029
1030 /**
1031 * Write the value of a given descriptor to the associated remote device.
1032 *
1033 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1034 * triggered to report the result of the write operation.
1035 *
1036 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1037 *
1038 * @param descriptor Descriptor to write to the associated remote device
1039 * @return true, if the write operation was initiated successfully
1040 */
1041 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1042 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1043 if (mService == null || mClientIf == 0) return false;
1044
1045 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1046 if (characteristic == null) return false;
1047
1048 BluetoothGattService service = characteristic.getService();
1049 if (service == null) return false;
1050
1051 BluetoothDevice device = service.getDevice();
1052 if (device == null) return false;
1053
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001054 synchronized(mDeviceBusy) {
1055 if (mDeviceBusy) return false;
1056 mDeviceBusy = true;
1057 }
1058
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001059 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001060 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1061 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1062 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1063 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001064 characteristic.getWriteType(), AUTHENTICATION_NONE,
1065 descriptor.getValue());
1066 } catch (RemoteException e) {
1067 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001068 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001069 return false;
1070 }
1071
1072 return true;
1073 }
1074
1075 /**
1076 * Initiates a reliable write transaction for a given remote device.
1077 *
1078 * <p>Once a reliable write transaction has been initiated, all calls
1079 * to {@link #writeCharacteristic} are sent to the remote device for
1080 * verification and queued up for atomic execution. The application will
1081 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1082 * in response to every {@link #writeCharacteristic} call and is responsible
1083 * for verifying if the value has been transmitted accurately.
1084 *
1085 * <p>After all characteristics have been queued up and verified,
1086 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1087 * was not written correctly, calling {@link #abortReliableWrite} will
1088 * cancel the current transaction without commiting any values on the
1089 * remote device.
1090 *
1091 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1092 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001093 * @return true, if the reliable write transaction has been initiated
1094 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001095 public boolean beginReliableWrite() {
1096 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001097 if (mService == null || mClientIf == 0) return false;
1098
1099 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001100 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001101 } catch (RemoteException e) {
1102 Log.e(TAG,"",e);
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109 /**
1110 * Executes a reliable write transaction for a given remote device.
1111 *
1112 * <p>This function will commit all queued up characteristic write
1113 * operations for a given remote device.
1114 *
1115 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1116 * invoked to indicate whether the transaction has been executed correctly.
1117 *
1118 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1119 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001120 * @return true, if the request to execute the transaction has been sent
1121 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001122 public boolean executeReliableWrite() {
1123 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 if (mService == null || mClientIf == 0) return false;
1125
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001126 synchronized(mDeviceBusy) {
1127 if (mDeviceBusy) return false;
1128 mDeviceBusy = true;
1129 }
1130
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001131 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001132 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001133 } catch (RemoteException e) {
1134 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001135 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001136 return false;
1137 }
1138
1139 return true;
1140 }
1141
1142 /**
1143 * Cancels a reliable write transaction for a given device.
1144 *
1145 * <p>Calling this function will discard all queued characteristic write
1146 * operations for a given remote device.
1147 *
1148 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001149 */
John Du48f8b5d2013-08-19 12:20:37 -07001150 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001151 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001152 if (mService == null || mClientIf == 0) return;
1153
1154 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001155 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001156 } catch (RemoteException e) {
1157 Log.e(TAG,"",e);
1158 }
1159 }
1160
1161 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001162 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001163 */
1164 public void abortReliableWrite(BluetoothDevice mDevice) {
1165 abortReliableWrite();
1166 }
1167
1168 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001169 * Enable or disable notifications/indications for a given characteristic.
1170 *
1171 * <p>Once notifications are enabled for a characteristic, a
1172 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1173 * triggered if the remote device indicates that the given characteristic
1174 * has changed.
1175 *
1176 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1177 *
1178 * @param characteristic The characteristic for which to enable notifications
1179 * @param enable Set to true to enable notifications/indications
1180 * @return true, if the requested notification status was set successfully
1181 */
1182 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1183 boolean enable) {
1184 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1185 + " enable: " + enable);
1186 if (mService == null || mClientIf == 0) return false;
1187
1188 BluetoothGattService service = characteristic.getService();
1189 if (service == null) return false;
1190
1191 BluetoothDevice device = service.getDevice();
1192 if (device == null) return false;
1193
1194 try {
1195 mService.registerForNotification(mClientIf, device.getAddress(),
1196 service.getType(), service.getInstanceId(),
1197 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1198 new ParcelUuid(characteristic.getUuid()),
1199 enable);
1200 } catch (RemoteException e) {
1201 Log.e(TAG,"",e);
1202 return false;
1203 }
1204
1205 return true;
1206 }
1207
1208 /**
1209 * Clears the internal cache and forces a refresh of the services from the
1210 * remote device.
1211 * @hide
1212 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001213 public boolean refresh() {
1214 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001215 if (mService == null || mClientIf == 0) return false;
1216
1217 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001218 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001219 } catch (RemoteException e) {
1220 Log.e(TAG,"",e);
1221 return false;
1222 }
1223
1224 return true;
1225 }
1226
1227 /**
1228 * Read the RSSI for a connected remote device.
1229 *
1230 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1231 * invoked when the RSSI value has been read.
1232 *
1233 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1234 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001235 * @return true, if the RSSI value has been requested successfully
1236 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001237 public boolean readRemoteRssi() {
1238 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001239 if (mService == null || mClientIf == 0) return false;
1240
1241 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001242 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001243 } catch (RemoteException e) {
1244 Log.e(TAG,"",e);
1245 return false;
1246 }
1247
1248 return true;
1249 }
1250
1251 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001252 * Configure the MTU used for a given connection.
1253 *
1254 * <p>When performing a write request operation (write without response),
1255 * the data sent is truncated to the MTU size. This function may be used
1256 * to request a larget MTU size to be able to send more data at once.
1257 *
1258 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1259 * whether this operation was successful.
1260 *
1261 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1262 *
1263 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001264 */
1265 public boolean configureMTU(int mtu) {
1266 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1267 + " mtu: " + mtu);
1268 if (mService == null || mClientIf == 0) return false;
1269
1270 try {
1271 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1272 } catch (RemoteException e) {
1273 Log.e(TAG,"",e);
1274 return false;
1275 }
1276
1277 return true;
1278 }
1279
1280 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001281 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1282 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001283 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001284 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001285 */
1286 @Override
1287 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001288 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001289 }
1290
1291 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001292 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1293 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001294 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001295 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001296 */
1297 @Override
1298 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001299 throw new UnsupportedOperationException
1300 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001301 }
1302
1303 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001304 * Not supported - please use
1305 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1306 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001307 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001308 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001309 */
1310 @Override
1311 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001312 throw new UnsupportedOperationException
1313 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001314 }
1315}