blob: a287b3c4925f45822dd25bf60b2f250c458b7220 [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 }
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700634
635 /**
636 * @hide
637 */
638 @Override
639 public void onFoundOrLost(boolean onFound, String address, int rssi,
640 byte[] advData) {
641 // no op.
642 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800643 };
644
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700645 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
646 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800647 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800648 mService = iGatt;
649 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700650 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800651 mServices = new ArrayList<BluetoothGattService>();
652
Matthew Xieddf7e472013-03-01 18:41:02 -0800653 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800654 }
655
656 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700657 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700658 *
659 * Application should call this method as early as possible after it is done with
660 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800661 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700662 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800663 if (DBG) Log.d(TAG, "close()");
664
665 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700666 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800667 }
668
669 /**
670 * Returns a service by UUID, instance and type.
671 * @hide
672 */
673 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
674 int instanceId, int type) {
675 for(BluetoothGattService svc : mServices) {
676 if (svc.getDevice().equals(device) &&
677 svc.getType() == type &&
678 svc.getInstanceId() == instanceId &&
679 svc.getUuid().equals(uuid)) {
680 return svc;
681 }
682 }
683 return null;
684 }
685
686
687 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800688 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800689 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800690 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
691 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800692 *
693 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
694 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800695 * @param callback GATT callback handler that will receive asynchronous callbacks.
696 * @return If true, the callback will be called to notify success or failure,
697 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800698 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800699 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800700 if (DBG) Log.d(TAG, "registerApp()");
701 if (mService == null) return false;
702
703 mCallback = callback;
704 UUID uuid = UUID.randomUUID();
705 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
706
707 try {
708 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
709 } catch (RemoteException e) {
710 Log.e(TAG,"",e);
711 return false;
712 }
713
714 return true;
715 }
716
717 /**
718 * Unregister the current application and callbacks.
719 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800720 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
722 if (mService == null || mClientIf == 0) return;
723
724 try {
725 mCallback = null;
726 mService.unregisterClient(mClientIf);
727 mClientIf = 0;
728 } catch (RemoteException e) {
729 Log.e(TAG,"",e);
730 }
731 }
732
733 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800734 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800735 *
736 * <p>The connection may not be established right away, but will be
737 * completed when the remote device is available. A
738 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
739 * invoked when the connection state changes as a result of this function.
740 *
741 * <p>The autoConnect paramter determines whether to actively connect to
742 * the remote device, or rather passively scan and finalize the connection
743 * when the remote device is in range/available. Generally, the first ever
744 * connection to a device should be direct (autoConnect set to false) and
745 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800746 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800747 *
748 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
749 *
750 * @param device Remote device to connect to
751 * @param autoConnect Whether to directly connect to the remote device (false)
752 * or to automatically connect as soon as the remote
753 * device becomes available (true).
754 * @return true, if the connection attempt was initiated successfully
755 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800756 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
757 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
758 synchronized(mStateLock) {
759 if (mConnState != CONN_STATE_IDLE) {
760 throw new IllegalStateException("Not idle");
761 }
762 mConnState = CONN_STATE_CONNECTING;
763 }
764 if (!registerApp(callback)) {
765 synchronized(mStateLock) {
766 mConnState = CONN_STATE_IDLE;
767 }
768 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800769 return false;
770 }
771
Matthew Xieddf7e472013-03-01 18:41:02 -0800772 // the connection will continue after successful callback registration
773 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800774 return true;
775 }
776
777 /**
778 * Disconnects an established connection, or cancels a connection attempt
779 * currently in progress.
780 *
781 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800782 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800783 public void disconnect() {
784 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800785 if (mService == null || mClientIf == 0) return;
786
787 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800788 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800789 } catch (RemoteException e) {
790 Log.e(TAG,"",e);
791 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700792 }
793
794 /**
795 * Connect back to remote device.
796 *
797 * <p>This method is used to re-connect to a remote device after the
798 * connection has been dropped. If the device is not in range, the
799 * re-connection will be triggered once the device is back in range.
800 *
801 * @return true, if the connection attempt was initiated successfully
802 */
803 public boolean connect() {
804 try {
805 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700806 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700807 return true;
808 } catch (RemoteException e) {
809 Log.e(TAG,"",e);
810 return false;
811 }
812 }
813
814 /**
815 * Return the remote bluetooth device this GATT client targets to
816 *
817 * @return remote bluetooth device
818 */
819 public BluetoothDevice getDevice() {
820 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800821 }
822
823 /**
824 * Discovers services offered by a remote device as well as their
825 * characteristics and descriptors.
826 *
827 * <p>This is an asynchronous operation. Once service discovery is completed,
828 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
829 * triggered. If the discovery was successful, the remote services can be
830 * retrieved using the {@link #getServices} function.
831 *
832 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
833 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800834 * @return true, if the remote service discovery has been started
835 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800836 public boolean discoverServices() {
837 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800838 if (mService == null || mClientIf == 0) return false;
839
840 mServices.clear();
841
842 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800843 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800844 } catch (RemoteException e) {
845 Log.e(TAG,"",e);
846 return false;
847 }
848
849 return true;
850 }
851
852 /**
853 * Returns a list of GATT services offered by the remote device.
854 *
855 * <p>This function requires that service discovery has been completed
856 * for the given device.
857 *
858 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
859 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800860 * @return List of services on the remote device. Returns an empty list
861 * if service discovery has not yet been performed.
862 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800863 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800864 List<BluetoothGattService> result =
865 new ArrayList<BluetoothGattService>();
866
867 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800868 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800869 result.add(service);
870 }
871 }
872
873 return result;
874 }
875
876 /**
877 * Returns a {@link BluetoothGattService}, if the requested UUID is
878 * supported by the remote device.
879 *
880 * <p>This function requires that service discovery has been completed
881 * for the given device.
882 *
883 * <p>If multiple instances of the same service (as identified by UUID)
884 * exist, the first instance of the service is returned.
885 *
886 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
887 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800888 * @param uuid UUID of the requested service
889 * @return BluetoothGattService if supported, or null if the requested
890 * service is not offered by the remote device.
891 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800892 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800893 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800894 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800895 service.getUuid().equals(uuid)) {
896 return service;
897 }
898 }
899
900 return null;
901 }
902
903 /**
904 * Reads the requested characteristic from the associated remote device.
905 *
906 * <p>This is an asynchronous operation. The result of the read operation
907 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
908 * callback.
909 *
910 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
911 *
912 * @param characteristic Characteristic to read from the remote device
913 * @return true, if the read operation was initiated successfully
914 */
915 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
916 if ((characteristic.getProperties() &
917 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
918
919 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
920 if (mService == null || mClientIf == 0) return false;
921
922 BluetoothGattService service = characteristic.getService();
923 if (service == null) return false;
924
925 BluetoothDevice device = service.getDevice();
926 if (device == null) return false;
927
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700928 synchronized(mDeviceBusy) {
929 if (mDeviceBusy) return false;
930 mDeviceBusy = true;
931 }
932
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800933 try {
934 mService.readCharacteristic(mClientIf, device.getAddress(),
935 service.getType(), service.getInstanceId(),
936 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
937 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
938 } catch (RemoteException e) {
939 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700940 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800941 return false;
942 }
943
944 return true;
945 }
946
947 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800948 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800949 *
950 * <p>Once the write operation has been completed, the
951 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
952 * reporting the result of the operation.
953 *
954 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
955 *
956 * @param characteristic Characteristic to write on the remote device
957 * @return true, if the write operation was initiated successfully
958 */
959 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
960 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
961 && (characteristic.getProperties() &
962 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
963
964 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
965 if (mService == null || mClientIf == 0) return false;
966
967 BluetoothGattService service = characteristic.getService();
968 if (service == null) return false;
969
970 BluetoothDevice device = service.getDevice();
971 if (device == null) return false;
972
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700973 synchronized(mDeviceBusy) {
974 if (mDeviceBusy) return false;
975 mDeviceBusy = true;
976 }
977
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800978 try {
979 mService.writeCharacteristic(mClientIf, device.getAddress(),
980 service.getType(), service.getInstanceId(),
981 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
982 new ParcelUuid(characteristic.getUuid()),
983 characteristic.getWriteType(), AUTHENTICATION_NONE,
984 characteristic.getValue());
985 } catch (RemoteException e) {
986 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700987 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800988 return false;
989 }
990
991 return true;
992 }
993
994 /**
995 * Reads the value for a given descriptor from the associated remote device.
996 *
997 * <p>Once the read operation has been completed, the
998 * {@link BluetoothGattCallback#onDescriptorRead} callback is
999 * triggered, signaling the result of the operation.
1000 *
1001 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1002 *
1003 * @param descriptor Descriptor value to read from the remote device
1004 * @return true, if the read operation was initiated successfully
1005 */
1006 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
1007 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
1008 if (mService == null || mClientIf == 0) return false;
1009
1010 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1011 if (characteristic == null) return false;
1012
1013 BluetoothGattService service = characteristic.getService();
1014 if (service == null) return false;
1015
1016 BluetoothDevice device = service.getDevice();
1017 if (device == null) return false;
1018
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001019 synchronized(mDeviceBusy) {
1020 if (mDeviceBusy) return false;
1021 mDeviceBusy = true;
1022 }
1023
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001024 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001025 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
1026 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1027 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1028 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
1029 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001030 } catch (RemoteException e) {
1031 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001032 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001033 return false;
1034 }
1035
1036 return true;
1037 }
1038
1039 /**
1040 * Write the value of a given descriptor to the associated remote device.
1041 *
1042 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1043 * triggered to report the result of the write operation.
1044 *
1045 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1046 *
1047 * @param descriptor Descriptor to write to the associated remote device
1048 * @return true, if the write operation was initiated successfully
1049 */
1050 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1051 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1052 if (mService == null || mClientIf == 0) return false;
1053
1054 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1055 if (characteristic == null) return false;
1056
1057 BluetoothGattService service = characteristic.getService();
1058 if (service == null) return false;
1059
1060 BluetoothDevice device = service.getDevice();
1061 if (device == null) return false;
1062
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001063 synchronized(mDeviceBusy) {
1064 if (mDeviceBusy) return false;
1065 mDeviceBusy = true;
1066 }
1067
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001068 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001069 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1070 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1071 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1072 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001073 characteristic.getWriteType(), AUTHENTICATION_NONE,
1074 descriptor.getValue());
1075 } catch (RemoteException e) {
1076 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001077 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001078 return false;
1079 }
1080
1081 return true;
1082 }
1083
1084 /**
1085 * Initiates a reliable write transaction for a given remote device.
1086 *
1087 * <p>Once a reliable write transaction has been initiated, all calls
1088 * to {@link #writeCharacteristic} are sent to the remote device for
1089 * verification and queued up for atomic execution. The application will
1090 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1091 * in response to every {@link #writeCharacteristic} call and is responsible
1092 * for verifying if the value has been transmitted accurately.
1093 *
1094 * <p>After all characteristics have been queued up and verified,
1095 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1096 * was not written correctly, calling {@link #abortReliableWrite} will
1097 * cancel the current transaction without commiting any values on the
1098 * remote device.
1099 *
1100 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1101 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001102 * @return true, if the reliable write transaction has been initiated
1103 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001104 public boolean beginReliableWrite() {
1105 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001106 if (mService == null || mClientIf == 0) return false;
1107
1108 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001109 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001110 } catch (RemoteException e) {
1111 Log.e(TAG,"",e);
1112 return false;
1113 }
1114
1115 return true;
1116 }
1117
1118 /**
1119 * Executes a reliable write transaction for a given remote device.
1120 *
1121 * <p>This function will commit all queued up characteristic write
1122 * operations for a given remote device.
1123 *
1124 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1125 * invoked to indicate whether the transaction has been executed correctly.
1126 *
1127 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1128 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001129 * @return true, if the request to execute the transaction has been sent
1130 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001131 public boolean executeReliableWrite() {
1132 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001133 if (mService == null || mClientIf == 0) return false;
1134
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001135 synchronized(mDeviceBusy) {
1136 if (mDeviceBusy) return false;
1137 mDeviceBusy = true;
1138 }
1139
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001140 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001141 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001142 } catch (RemoteException e) {
1143 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001144 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001145 return false;
1146 }
1147
1148 return true;
1149 }
1150
1151 /**
1152 * Cancels a reliable write transaction for a given device.
1153 *
1154 * <p>Calling this function will discard all queued characteristic write
1155 * operations for a given remote device.
1156 *
1157 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001158 */
John Du48f8b5d2013-08-19 12:20:37 -07001159 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001160 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001161 if (mService == null || mClientIf == 0) return;
1162
1163 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001164 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001165 } catch (RemoteException e) {
1166 Log.e(TAG,"",e);
1167 }
1168 }
1169
1170 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001171 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001172 */
1173 public void abortReliableWrite(BluetoothDevice mDevice) {
1174 abortReliableWrite();
1175 }
1176
1177 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001178 * Enable or disable notifications/indications for a given characteristic.
1179 *
1180 * <p>Once notifications are enabled for a characteristic, a
1181 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1182 * triggered if the remote device indicates that the given characteristic
1183 * has changed.
1184 *
1185 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1186 *
1187 * @param characteristic The characteristic for which to enable notifications
1188 * @param enable Set to true to enable notifications/indications
1189 * @return true, if the requested notification status was set successfully
1190 */
1191 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1192 boolean enable) {
1193 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1194 + " enable: " + enable);
1195 if (mService == null || mClientIf == 0) return false;
1196
1197 BluetoothGattService service = characteristic.getService();
1198 if (service == null) return false;
1199
1200 BluetoothDevice device = service.getDevice();
1201 if (device == null) return false;
1202
1203 try {
1204 mService.registerForNotification(mClientIf, device.getAddress(),
1205 service.getType(), service.getInstanceId(),
1206 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1207 new ParcelUuid(characteristic.getUuid()),
1208 enable);
1209 } catch (RemoteException e) {
1210 Log.e(TAG,"",e);
1211 return false;
1212 }
1213
1214 return true;
1215 }
1216
1217 /**
1218 * Clears the internal cache and forces a refresh of the services from the
1219 * remote device.
1220 * @hide
1221 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001222 public boolean refresh() {
1223 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001224 if (mService == null || mClientIf == 0) return false;
1225
1226 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001227 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001228 } catch (RemoteException e) {
1229 Log.e(TAG,"",e);
1230 return false;
1231 }
1232
1233 return true;
1234 }
1235
1236 /**
1237 * Read the RSSI for a connected remote device.
1238 *
1239 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1240 * invoked when the RSSI value has been read.
1241 *
1242 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1243 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001244 * @return true, if the RSSI value has been requested successfully
1245 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001246 public boolean readRemoteRssi() {
1247 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001248 if (mService == null || mClientIf == 0) return false;
1249
1250 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001251 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001252 } catch (RemoteException e) {
1253 Log.e(TAG,"",e);
1254 return false;
1255 }
1256
1257 return true;
1258 }
1259
1260 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001261 * Configure the MTU used for a given connection.
1262 *
1263 * <p>When performing a write request operation (write without response),
1264 * the data sent is truncated to the MTU size. This function may be used
1265 * to request a larget MTU size to be able to send more data at once.
1266 *
1267 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1268 * whether this operation was successful.
1269 *
1270 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1271 *
1272 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001273 */
1274 public boolean configureMTU(int mtu) {
1275 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1276 + " mtu: " + mtu);
1277 if (mService == null || mClientIf == 0) return false;
1278
1279 try {
1280 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1281 } catch (RemoteException e) {
1282 Log.e(TAG,"",e);
1283 return false;
1284 }
1285
1286 return true;
1287 }
1288
1289 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001290 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1291 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001292 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001293 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001294 */
1295 @Override
1296 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001297 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001298 }
1299
1300 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001301 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1302 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001303 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001304 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001305 */
1306 @Override
1307 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001308 throw new UnsupportedOperationException
1309 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001310 }
1311
1312 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001313 * Not supported - please use
1314 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1315 * with {@link BluetoothProfile#GATT} as first 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> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001321 throw new UnsupportedOperationException
1322 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001323 }
1324}