blob: f8684e1474bd7430ca6f4db878a4ecfd0ead1648 [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 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070096 * Connection paramter update - Use the connection paramters recommended by the
97 * Bluetooth SIG. This is the default value if no connection parameter update
98 * is requested.
99 */
100 public static final int GATT_CONNECTION_BALANCED = 0;
101
102 /**
103 * Connection paramter update - Request a high priority, low latency connection.
104 * An application should only request high priority connection paramters to transfer
105 * large amounts of data over LE quickly. Once the transfer is complete, the application
106 * should request {@link BluetoothGatt#GATT_CONNECTION_BALANCED} connectoin parameters
107 * to reduce energy use.
108 */
109 public static final int GATT_CONNECTION_HIGH_PRIORITY = 1;
110
111 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
112 public static final int GATT_CONNECTION_LOW_POWER = 2;
113
114 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800115 * No authentication required.
116 * @hide
117 */
118 /*package*/ static final int AUTHENTICATION_NONE = 0;
119
120 /**
121 * Authentication requested; no man-in-the-middle protection required.
122 * @hide
123 */
124 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
125
126 /**
127 * Authentication with man-in-the-middle protection requested.
128 * @hide
129 */
130 /*package*/ static final int AUTHENTICATION_MITM = 2;
131
132 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800133 * Bluetooth GATT interface callbacks
134 */
135 private final IBluetoothGattCallback mBluetoothGattCallback =
136 new IBluetoothGattCallback.Stub() {
137 /**
138 * Application interface registered - app is ready to go
139 * @hide
140 */
141 public void onClientRegistered(int status, int clientIf) {
142 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
143 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800144 if (VDBG) {
145 synchronized(mStateLock) {
146 if (mConnState != CONN_STATE_CONNECTING) {
147 Log.e(TAG, "Bad connection state: " + mConnState);
148 }
149 }
150 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800151 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800152 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700153 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800154 BluetoothProfile.STATE_DISCONNECTED);
155 synchronized(mStateLock) {
156 mConnState = CONN_STATE_IDLE;
157 }
158 return;
159 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800160 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800161 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700162 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800163 } catch (RemoteException e) {
164 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800165 }
166 }
167
168 /**
169 * Client connection state changed
170 * @hide
171 */
172 public void onClientConnectionState(int status, int clientIf,
173 boolean connected, String address) {
174 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
175 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800176 if (!address.equals(mDevice.getAddress())) {
177 return;
178 }
179 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
180 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800181 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700182 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800183 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700184 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800185 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800186
187 synchronized(mStateLock) {
188 if (connected) {
189 mConnState = CONN_STATE_CONNECTED;
190 } else {
191 mConnState = CONN_STATE_IDLE;
192 }
193 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700194
195 synchronized(mDeviceBusy) {
196 mDeviceBusy = false;
197 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800198 }
199
200 /**
201 * Callback reporting an LE scan result.
202 * @hide
203 */
204 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800205 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800206 }
207
208 /**
209 * A new GATT service has been discovered.
210 * The service is added to the internal list and the search
211 * continues.
212 * @hide
213 */
214 public void onGetService(String address, int srvcType,
215 int srvcInstId, ParcelUuid srvcUuid) {
216 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800217 if (!address.equals(mDevice.getAddress())) {
218 return;
219 }
220 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800221 srvcInstId, srvcType));
222 }
223
224 /**
225 * An included service has been found durig GATT discovery.
226 * The included service is added to the respective parent.
227 * @hide
228 */
229 public void onGetIncludedService(String address, int srvcType,
230 int srvcInstId, ParcelUuid srvcUuid,
231 int inclSrvcType, int inclSrvcInstId,
232 ParcelUuid inclSrvcUuid) {
233 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
234 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
235
Matthew Xieddf7e472013-03-01 18:41:02 -0800236 if (!address.equals(mDevice.getAddress())) {
237 return;
238 }
239 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800240 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800241 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800242 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
243
244 if (service != null && includedService != null) {
245 service.addIncludedService(includedService);
246 }
247 }
248
249 /**
250 * A new GATT characteristic has been discovered.
251 * Add the new characteristic to the relevant service and continue
252 * the remote device inspection.
253 * @hide
254 */
255 public void onGetCharacteristic(String address, int srvcType,
256 int srvcInstId, ParcelUuid srvcUuid,
257 int charInstId, ParcelUuid charUuid,
258 int charProps) {
259 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
260 charUuid);
261
Matthew Xieddf7e472013-03-01 18:41:02 -0800262 if (!address.equals(mDevice.getAddress())) {
263 return;
264 }
265 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800266 srvcInstId, srvcType);
267 if (service != null) {
268 service.addCharacteristic(new BluetoothGattCharacteristic(
269 service, charUuid.getUuid(), charInstId, charProps, 0));
270 }
271 }
272
273 /**
274 * A new GATT descriptor has been discovered.
275 * Finally, add the descriptor to the related characteristic.
276 * This should conclude the remote device update.
277 * @hide
278 */
279 public void onGetDescriptor(String address, int srvcType,
280 int srvcInstId, ParcelUuid srvcUuid,
281 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700282 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800283 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
284
Matthew Xieddf7e472013-03-01 18:41:02 -0800285 if (!address.equals(mDevice.getAddress())) {
286 return;
287 }
288 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800289 srvcInstId, srvcType);
290 if (service == null) return;
291
292 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
Mike J. Chen2975c682014-06-24 10:19:45 -0700293 charUuid.getUuid(), charInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800294 if (characteristic == null) return;
295
296 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700297 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800298 }
299
300 /**
301 * Remote search has been completed.
302 * The internal object structure should now reflect the state
303 * of the remote device database. Let the application know that
304 * we are done at this point.
305 * @hide
306 */
307 public void onSearchComplete(String address, int status) {
308 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800309 if (!address.equals(mDevice.getAddress())) {
310 return;
311 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800312 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700313 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800314 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700315 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 }
317 }
318
319 /**
320 * Remote characteristic has been read.
321 * Updates the internal value.
322 * @hide
323 */
324 public void onCharacteristicRead(String address, int status, int srvcType,
325 int srvcInstId, ParcelUuid srvcUuid,
326 int charInstId, ParcelUuid charUuid, byte[] value) {
327 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
328 + " UUID=" + charUuid + " Status=" + status);
329
Matthew Xieddf7e472013-03-01 18:41:02 -0800330 if (!address.equals(mDevice.getAddress())) {
331 return;
332 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700333
334 synchronized(mDeviceBusy) {
335 mDeviceBusy = false;
336 }
337
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800338 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
339 || status == GATT_INSUFFICIENT_ENCRYPTION)
340 && mAuthRetry == false) {
341 try {
342 mAuthRetry = true;
343 mService.readCharacteristic(mClientIf, address,
344 srvcType, srvcInstId, srvcUuid,
345 charInstId, charUuid, AUTHENTICATION_MITM);
346 return;
347 } catch (RemoteException e) {
348 Log.e(TAG,"",e);
349 }
350 }
351
352 mAuthRetry = false;
353
Matthew Xieddf7e472013-03-01 18:41:02 -0800354 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800355 srvcInstId, srvcType);
356 if (service == null) return;
357
358 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
359 charUuid.getUuid(), charInstId);
360 if (characteristic == null) return;
361
362 if (status == 0) characteristic.setValue(value);
363
364 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700365 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800366 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700367 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800368 }
369 }
370
371 /**
372 * Characteristic has been written to the remote device.
373 * Let the app know how we did...
374 * @hide
375 */
376 public void onCharacteristicWrite(String address, int status, int srvcType,
377 int srvcInstId, ParcelUuid srvcUuid,
378 int charInstId, ParcelUuid charUuid) {
379 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
380 + " UUID=" + charUuid + " Status=" + status);
381
Matthew Xieddf7e472013-03-01 18:41:02 -0800382 if (!address.equals(mDevice.getAddress())) {
383 return;
384 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700385
386 synchronized(mDeviceBusy) {
387 mDeviceBusy = false;
388 }
389
Matthew Xieddf7e472013-03-01 18:41:02 -0800390 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800391 srvcInstId, srvcType);
392 if (service == null) return;
393
394 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
395 charUuid.getUuid(), charInstId);
396 if (characteristic == null) return;
397
398 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
399 || status == GATT_INSUFFICIENT_ENCRYPTION)
400 && mAuthRetry == false) {
401 try {
402 mAuthRetry = true;
403 mService.writeCharacteristic(mClientIf, address,
404 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
405 characteristic.getWriteType(), AUTHENTICATION_MITM,
406 characteristic.getValue());
407 return;
408 } catch (RemoteException e) {
409 Log.e(TAG,"",e);
410 }
411 }
412
413 mAuthRetry = false;
414
415 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700416 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800417 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700418 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800419 }
420 }
421
422 /**
423 * Remote characteristic has been updated.
424 * Updates the internal value.
425 * @hide
426 */
427 public void onNotify(String address, int srvcType,
428 int srvcInstId, ParcelUuid srvcUuid,
429 int charInstId, ParcelUuid charUuid,
430 byte[] value) {
431 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
432
Matthew Xieddf7e472013-03-01 18:41:02 -0800433 if (!address.equals(mDevice.getAddress())) {
434 return;
435 }
436 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800437 srvcInstId, srvcType);
438 if (service == null) return;
439
440 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
441 charUuid.getUuid(), charInstId);
442 if (characteristic == null) return;
443
444 characteristic.setValue(value);
445
446 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700447 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800448 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700449 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800450 }
451 }
452
453 /**
454 * Descriptor has been read.
455 * @hide
456 */
457 public void onDescriptorRead(String address, int status, int srvcType,
458 int srvcInstId, ParcelUuid srvcUuid,
459 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700460 int descrInstId, ParcelUuid descrUuid,
461 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800462 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
463
Matthew Xieddf7e472013-03-01 18:41:02 -0800464 if (!address.equals(mDevice.getAddress())) {
465 return;
466 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700467
468 synchronized(mDeviceBusy) {
469 mDeviceBusy = false;
470 }
471
Matthew Xieddf7e472013-03-01 18:41:02 -0800472 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800473 srvcInstId, srvcType);
474 if (service == null) return;
475
476 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
477 charUuid.getUuid(), charInstId);
478 if (characteristic == null) return;
479
480 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700481 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800482 if (descriptor == null) return;
483
484 if (status == 0) descriptor.setValue(value);
485
486 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
487 || status == GATT_INSUFFICIENT_ENCRYPTION)
488 && mAuthRetry == false) {
489 try {
490 mAuthRetry = true;
491 mService.readDescriptor(mClientIf, address,
492 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700493 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800494 } catch (RemoteException e) {
495 Log.e(TAG,"",e);
496 }
497 }
498
499 mAuthRetry = true;
500
501 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700502 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800503 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700504 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800505 }
506 }
507
508 /**
509 * Descriptor write operation complete.
510 * @hide
511 */
512 public void onDescriptorWrite(String address, int status, int srvcType,
513 int srvcInstId, ParcelUuid srvcUuid,
514 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700515 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800516 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
517
Matthew Xieddf7e472013-03-01 18:41:02 -0800518 if (!address.equals(mDevice.getAddress())) {
519 return;
520 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700521
522 synchronized(mDeviceBusy) {
523 mDeviceBusy = false;
524 }
525
Matthew Xieddf7e472013-03-01 18:41:02 -0800526 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800527 srvcInstId, srvcType);
528 if (service == null) return;
529
530 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
531 charUuid.getUuid(), charInstId);
532 if (characteristic == null) return;
533
534 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700535 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800536 if (descriptor == null) return;
537
538 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
539 || status == GATT_INSUFFICIENT_ENCRYPTION)
540 && mAuthRetry == false) {
541 try {
542 mAuthRetry = true;
543 mService.writeDescriptor(mClientIf, address,
544 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700545 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800546 AUTHENTICATION_MITM, descriptor.getValue());
547 } catch (RemoteException e) {
548 Log.e(TAG,"",e);
549 }
550 }
551
552 mAuthRetry = false;
553
554 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700555 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800556 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700557 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800558 }
559 }
560
561 /**
562 * Prepared write transaction completed (or aborted)
563 * @hide
564 */
565 public void onExecuteWrite(String address, int status) {
566 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
567 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800568 if (!address.equals(mDevice.getAddress())) {
569 return;
570 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700571
572 synchronized(mDeviceBusy) {
573 mDeviceBusy = false;
574 }
575
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800576 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700577 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800578 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700579 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 }
581 }
582
583 /**
584 * Remote device RSSI has been read
585 * @hide
586 */
587 public void onReadRemoteRssi(String address, int rssi, int status) {
588 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
589 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800590 if (!address.equals(mDevice.getAddress())) {
591 return;
592 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800593 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700594 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800595 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700596 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800597 }
598 }
Wei Wangf3055892014-03-11 22:22:41 -0700599
600 /**
601 * Advertise state change callback
602 * @hide
603 */
604 public void onAdvertiseStateChange(int state, int status) {
605 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
606 + state + " status=" + status);
Wei Wangadf6aff2014-05-20 06:30:20 +0000607 }
608
609 /**
610 * @hide
611 */
612 @Override
613 public void onMultiAdvertiseCallback(int status) {
614 // no op.
615 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700616
617 /**
618 * Callback invoked when the MTU for a given connection changes
619 * @hide
620 */
621 public void onConfigureMTU(String address, int mtu, int status) {
622 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
623 " mtu=" + mtu + " status=" + status);
624 if (!address.equals(mDevice.getAddress())) {
625 return;
626 }
627 try {
628 mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status);
629 } catch (Exception ex) {
630 Log.w(TAG, "Unhandled exception in callback", ex);
631 }
Wei Wangf3055892014-03-11 22:22:41 -0700632 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700633
634 /**
635 * Callback indicating the remote device connection is congested.
636 * @hide
637 */
638 public void onConnectionCongested(String address, boolean congested) {
639 if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address
640 + " congested=" + congested);
641 if (!address.equals(mDevice.getAddress())) return;
642 try {
643 mCallback.onConnectionCongested(BluetoothGatt.this, congested);
644 } catch (Exception ex) {
645 Log.w(TAG, "Unhandled exception in callback", ex);
646 }
647 }
Wei Wang9fb17912014-07-01 15:10:06 -0700648
649 @Override
650 public void onBatchScanResults(List<ScanResult> results) {
651 // no op
652 }
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700653
654 /**
655 * @hide
656 */
657 @Override
658 public void onFoundOrLost(boolean onFound, String address, int rssi,
659 byte[] advData) {
660 // no op.
661 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800662 };
663
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700664 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
665 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800666 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800667 mService = iGatt;
668 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700669 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800670 mServices = new ArrayList<BluetoothGattService>();
671
Matthew Xieddf7e472013-03-01 18:41:02 -0800672 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800673 }
674
675 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700676 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700677 *
678 * Application should call this method as early as possible after it is done with
679 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800680 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700681 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682 if (DBG) Log.d(TAG, "close()");
683
684 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700685 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800686 }
687
688 /**
689 * Returns a service by UUID, instance and type.
690 * @hide
691 */
692 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
693 int instanceId, int type) {
694 for(BluetoothGattService svc : mServices) {
695 if (svc.getDevice().equals(device) &&
696 svc.getType() == type &&
697 svc.getInstanceId() == instanceId &&
698 svc.getUuid().equals(uuid)) {
699 return svc;
700 }
701 }
702 return null;
703 }
704
705
706 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800707 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800708 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800709 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
710 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800711 *
712 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
713 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800714 * @param callback GATT callback handler that will receive asynchronous callbacks.
715 * @return If true, the callback will be called to notify success or failure,
716 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800717 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800718 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800719 if (DBG) Log.d(TAG, "registerApp()");
720 if (mService == null) return false;
721
722 mCallback = callback;
723 UUID uuid = UUID.randomUUID();
724 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
725
726 try {
727 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
728 } catch (RemoteException e) {
729 Log.e(TAG,"",e);
730 return false;
731 }
732
733 return true;
734 }
735
736 /**
737 * Unregister the current application and callbacks.
738 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800739 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800740 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
741 if (mService == null || mClientIf == 0) return;
742
743 try {
744 mCallback = null;
745 mService.unregisterClient(mClientIf);
746 mClientIf = 0;
747 } catch (RemoteException e) {
748 Log.e(TAG,"",e);
749 }
750 }
751
752 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800753 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800754 *
755 * <p>The connection may not be established right away, but will be
756 * completed when the remote device is available. A
757 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
758 * invoked when the connection state changes as a result of this function.
759 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700760 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800761 * the remote device, or rather passively scan and finalize the connection
762 * when the remote device is in range/available. Generally, the first ever
763 * connection to a device should be direct (autoConnect set to false) and
764 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800765 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800766 *
767 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
768 *
769 * @param device Remote device to connect to
770 * @param autoConnect Whether to directly connect to the remote device (false)
771 * or to automatically connect as soon as the remote
772 * device becomes available (true).
773 * @return true, if the connection attempt was initiated successfully
774 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800775 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
776 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
777 synchronized(mStateLock) {
778 if (mConnState != CONN_STATE_IDLE) {
779 throw new IllegalStateException("Not idle");
780 }
781 mConnState = CONN_STATE_CONNECTING;
782 }
783 if (!registerApp(callback)) {
784 synchronized(mStateLock) {
785 mConnState = CONN_STATE_IDLE;
786 }
787 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800788 return false;
789 }
790
Matthew Xieddf7e472013-03-01 18:41:02 -0800791 // the connection will continue after successful callback registration
792 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800793 return true;
794 }
795
796 /**
797 * Disconnects an established connection, or cancels a connection attempt
798 * currently in progress.
799 *
800 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800801 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800802 public void disconnect() {
803 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800804 if (mService == null || mClientIf == 0) return;
805
806 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800807 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800808 } catch (RemoteException e) {
809 Log.e(TAG,"",e);
810 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700811 }
812
813 /**
814 * Connect back to remote device.
815 *
816 * <p>This method is used to re-connect to a remote device after the
817 * connection has been dropped. If the device is not in range, the
818 * re-connection will be triggered once the device is back in range.
819 *
820 * @return true, if the connection attempt was initiated successfully
821 */
822 public boolean connect() {
823 try {
824 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700825 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700826 return true;
827 } catch (RemoteException e) {
828 Log.e(TAG,"",e);
829 return false;
830 }
831 }
832
833 /**
834 * Return the remote bluetooth device this GATT client targets to
835 *
836 * @return remote bluetooth device
837 */
838 public BluetoothDevice getDevice() {
839 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800840 }
841
842 /**
843 * Discovers services offered by a remote device as well as their
844 * characteristics and descriptors.
845 *
846 * <p>This is an asynchronous operation. Once service discovery is completed,
847 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
848 * triggered. If the discovery was successful, the remote services can be
849 * retrieved using the {@link #getServices} function.
850 *
851 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
852 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800853 * @return true, if the remote service discovery has been started
854 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800855 public boolean discoverServices() {
856 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800857 if (mService == null || mClientIf == 0) return false;
858
859 mServices.clear();
860
861 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800862 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800863 } catch (RemoteException e) {
864 Log.e(TAG,"",e);
865 return false;
866 }
867
868 return true;
869 }
870
871 /**
872 * Returns a list of GATT services offered by the remote device.
873 *
874 * <p>This function requires that service discovery has been completed
875 * for the given device.
876 *
877 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
878 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800879 * @return List of services on the remote device. Returns an empty list
880 * if service discovery has not yet been performed.
881 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800882 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800883 List<BluetoothGattService> result =
884 new ArrayList<BluetoothGattService>();
885
886 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800887 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800888 result.add(service);
889 }
890 }
891
892 return result;
893 }
894
895 /**
896 * Returns a {@link BluetoothGattService}, if the requested UUID is
897 * supported by the remote device.
898 *
899 * <p>This function requires that service discovery has been completed
900 * for the given device.
901 *
902 * <p>If multiple instances of the same service (as identified by UUID)
903 * exist, the first instance of the service is returned.
904 *
905 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
906 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800907 * @param uuid UUID of the requested service
908 * @return BluetoothGattService if supported, or null if the requested
909 * service is not offered by the remote device.
910 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800911 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800912 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800913 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800914 service.getUuid().equals(uuid)) {
915 return service;
916 }
917 }
918
919 return null;
920 }
921
922 /**
923 * Reads the requested characteristic from the associated remote device.
924 *
925 * <p>This is an asynchronous operation. The result of the read operation
926 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
927 * callback.
928 *
929 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
930 *
931 * @param characteristic Characteristic to read from the remote device
932 * @return true, if the read operation was initiated successfully
933 */
934 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
935 if ((characteristic.getProperties() &
936 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
937
938 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
939 if (mService == null || mClientIf == 0) return false;
940
941 BluetoothGattService service = characteristic.getService();
942 if (service == null) return false;
943
944 BluetoothDevice device = service.getDevice();
945 if (device == null) return false;
946
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700947 synchronized(mDeviceBusy) {
948 if (mDeviceBusy) return false;
949 mDeviceBusy = true;
950 }
951
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800952 try {
953 mService.readCharacteristic(mClientIf, device.getAddress(),
954 service.getType(), service.getInstanceId(),
955 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
956 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
957 } catch (RemoteException e) {
958 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700959 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800960 return false;
961 }
962
963 return true;
964 }
965
966 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800967 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800968 *
969 * <p>Once the write operation has been completed, the
970 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
971 * reporting the result of the operation.
972 *
973 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
974 *
975 * @param characteristic Characteristic to write on the remote device
976 * @return true, if the write operation was initiated successfully
977 */
978 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
979 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
980 && (characteristic.getProperties() &
981 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
982
983 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
984 if (mService == null || mClientIf == 0) return false;
985
986 BluetoothGattService service = characteristic.getService();
987 if (service == null) return false;
988
989 BluetoothDevice device = service.getDevice();
990 if (device == null) return false;
991
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700992 synchronized(mDeviceBusy) {
993 if (mDeviceBusy) return false;
994 mDeviceBusy = true;
995 }
996
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800997 try {
998 mService.writeCharacteristic(mClientIf, device.getAddress(),
999 service.getType(), service.getInstanceId(),
1000 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1001 new ParcelUuid(characteristic.getUuid()),
1002 characteristic.getWriteType(), AUTHENTICATION_NONE,
1003 characteristic.getValue());
1004 } catch (RemoteException e) {
1005 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001006 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001007 return false;
1008 }
1009
1010 return true;
1011 }
1012
1013 /**
1014 * Reads the value for a given descriptor from the associated remote device.
1015 *
1016 * <p>Once the read operation has been completed, the
1017 * {@link BluetoothGattCallback#onDescriptorRead} callback is
1018 * triggered, signaling the result of the operation.
1019 *
1020 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1021 *
1022 * @param descriptor Descriptor value to read from the remote device
1023 * @return true, if the read operation was initiated successfully
1024 */
1025 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
1026 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
1027 if (mService == null || mClientIf == 0) return false;
1028
1029 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1030 if (characteristic == null) return false;
1031
1032 BluetoothGattService service = characteristic.getService();
1033 if (service == null) return false;
1034
1035 BluetoothDevice device = service.getDevice();
1036 if (device == null) return false;
1037
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001038 synchronized(mDeviceBusy) {
1039 if (mDeviceBusy) return false;
1040 mDeviceBusy = true;
1041 }
1042
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001043 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001044 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
1045 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1046 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1047 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
1048 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001049 } catch (RemoteException e) {
1050 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001051 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001052 return false;
1053 }
1054
1055 return true;
1056 }
1057
1058 /**
1059 * Write the value of a given descriptor to the associated remote device.
1060 *
1061 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1062 * triggered to report the result of the write operation.
1063 *
1064 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1065 *
1066 * @param descriptor Descriptor to write to the associated remote device
1067 * @return true, if the write operation was initiated successfully
1068 */
1069 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1070 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1071 if (mService == null || mClientIf == 0) return false;
1072
1073 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1074 if (characteristic == null) return false;
1075
1076 BluetoothGattService service = characteristic.getService();
1077 if (service == null) return false;
1078
1079 BluetoothDevice device = service.getDevice();
1080 if (device == null) return false;
1081
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001082 synchronized(mDeviceBusy) {
1083 if (mDeviceBusy) return false;
1084 mDeviceBusy = true;
1085 }
1086
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001087 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001088 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1089 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1090 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1091 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001092 characteristic.getWriteType(), AUTHENTICATION_NONE,
1093 descriptor.getValue());
1094 } catch (RemoteException e) {
1095 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001096 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001097 return false;
1098 }
1099
1100 return true;
1101 }
1102
1103 /**
1104 * Initiates a reliable write transaction for a given remote device.
1105 *
1106 * <p>Once a reliable write transaction has been initiated, all calls
1107 * to {@link #writeCharacteristic} are sent to the remote device for
1108 * verification and queued up for atomic execution. The application will
1109 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1110 * in response to every {@link #writeCharacteristic} call and is responsible
1111 * for verifying if the value has been transmitted accurately.
1112 *
1113 * <p>After all characteristics have been queued up and verified,
1114 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1115 * was not written correctly, calling {@link #abortReliableWrite} will
1116 * cancel the current transaction without commiting any values on the
1117 * remote device.
1118 *
1119 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1120 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001121 * @return true, if the reliable write transaction has been initiated
1122 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001123 public boolean beginReliableWrite() {
1124 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001125 if (mService == null || mClientIf == 0) return false;
1126
1127 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001128 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001129 } catch (RemoteException e) {
1130 Log.e(TAG,"",e);
1131 return false;
1132 }
1133
1134 return true;
1135 }
1136
1137 /**
1138 * Executes a reliable write transaction for a given remote device.
1139 *
1140 * <p>This function will commit all queued up characteristic write
1141 * operations for a given remote device.
1142 *
1143 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1144 * invoked to indicate whether the transaction has been executed correctly.
1145 *
1146 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1147 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001148 * @return true, if the request to execute the transaction has been sent
1149 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001150 public boolean executeReliableWrite() {
1151 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001152 if (mService == null || mClientIf == 0) return false;
1153
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001154 synchronized(mDeviceBusy) {
1155 if (mDeviceBusy) return false;
1156 mDeviceBusy = true;
1157 }
1158
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001159 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001160 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001161 } catch (RemoteException e) {
1162 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001163 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001164 return false;
1165 }
1166
1167 return true;
1168 }
1169
1170 /**
1171 * Cancels a reliable write transaction for a given device.
1172 *
1173 * <p>Calling this function will discard all queued characteristic write
1174 * operations for a given remote device.
1175 *
1176 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001177 */
John Du48f8b5d2013-08-19 12:20:37 -07001178 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001179 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001180 if (mService == null || mClientIf == 0) return;
1181
1182 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001183 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001184 } catch (RemoteException e) {
1185 Log.e(TAG,"",e);
1186 }
1187 }
1188
1189 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001190 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001191 */
1192 public void abortReliableWrite(BluetoothDevice mDevice) {
1193 abortReliableWrite();
1194 }
1195
1196 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001197 * Enable or disable notifications/indications for a given characteristic.
1198 *
1199 * <p>Once notifications are enabled for a characteristic, a
1200 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1201 * triggered if the remote device indicates that the given characteristic
1202 * has changed.
1203 *
1204 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1205 *
1206 * @param characteristic The characteristic for which to enable notifications
1207 * @param enable Set to true to enable notifications/indications
1208 * @return true, if the requested notification status was set successfully
1209 */
1210 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1211 boolean enable) {
1212 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1213 + " enable: " + enable);
1214 if (mService == null || mClientIf == 0) return false;
1215
1216 BluetoothGattService service = characteristic.getService();
1217 if (service == null) return false;
1218
1219 BluetoothDevice device = service.getDevice();
1220 if (device == null) return false;
1221
1222 try {
1223 mService.registerForNotification(mClientIf, device.getAddress(),
1224 service.getType(), service.getInstanceId(),
1225 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1226 new ParcelUuid(characteristic.getUuid()),
1227 enable);
1228 } catch (RemoteException e) {
1229 Log.e(TAG,"",e);
1230 return false;
1231 }
1232
1233 return true;
1234 }
1235
1236 /**
1237 * Clears the internal cache and forces a refresh of the services from the
1238 * remote device.
1239 * @hide
1240 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001241 public boolean refresh() {
1242 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001243 if (mService == null || mClientIf == 0) return false;
1244
1245 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001246 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001247 } catch (RemoteException e) {
1248 Log.e(TAG,"",e);
1249 return false;
1250 }
1251
1252 return true;
1253 }
1254
1255 /**
1256 * Read the RSSI for a connected remote device.
1257 *
1258 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1259 * invoked when the RSSI value has been read.
1260 *
1261 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1262 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001263 * @return true, if the RSSI value has been requested successfully
1264 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001265 public boolean readRemoteRssi() {
1266 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001267 if (mService == null || mClientIf == 0) return false;
1268
1269 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001270 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001271 } catch (RemoteException e) {
1272 Log.e(TAG,"",e);
1273 return false;
1274 }
1275
1276 return true;
1277 }
1278
1279 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001280 * Configure the MTU used for a given connection.
1281 *
1282 * <p>When performing a write request operation (write without response),
1283 * the data sent is truncated to the MTU size. This function may be used
1284 * to request a larget MTU size to be able to send more data at once.
1285 *
1286 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1287 * whether this operation was successful.
1288 *
1289 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1290 *
1291 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001292 */
1293 public boolean configureMTU(int mtu) {
1294 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1295 + " mtu: " + mtu);
1296 if (mService == null || mClientIf == 0) return false;
1297
1298 try {
1299 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1300 } catch (RemoteException e) {
1301 Log.e(TAG,"",e);
1302 return false;
1303 }
1304
1305 return true;
1306 }
1307
1308 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001309 * Request a connection parameter update.
1310 *
1311 * <p>This function will send a connection parameter update request to the
1312 * remote device.
1313 *
1314 * @param connectionPriority Request a specific connection priority. Must be one of
1315 * {@link BluetoothGatt#GATT_CONNECTION_BALANCED},
1316 * {@link BluetoothGatt#GATT_CONNECTION_HIGH_PRIORITY}
1317 * or {@link BluetoothGatt#GATT_CONNECTION_LOW_POWER}.
1318 * @throws IllegalArgumentException If the parameters are outside of their
1319 * specified range.
1320 */
1321 public boolean requestConnectionParameterUpdate(int connectionPriority) {
1322 if (connectionPriority < GATT_CONNECTION_BALANCED ||
1323 connectionPriority > GATT_CONNECTION_LOW_POWER) {
1324 throw new IllegalArgumentException("connectionPriority not within valid range");
1325 }
1326
1327 if (DBG) Log.d(TAG, "requestConnectionParameterUpdate() - params: " + connectionPriority);
1328 if (mService == null || mClientIf == 0) return false;
1329
1330 try {
1331 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1332 } catch (RemoteException e) {
1333 Log.e(TAG,"",e);
1334 return false;
1335 }
1336
1337 return true;
1338 }
1339
1340 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001341 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1342 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001343 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001344 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001345 */
1346 @Override
1347 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001348 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001349 }
1350
1351 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001352 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1353 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001354 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001355 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001356 */
1357 @Override
1358 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001359 throw new UnsupportedOperationException
1360 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001361 }
1362
1363 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001364 * Not supported - please use
1365 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1366 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001367 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001368 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001369 */
1370 @Override
1371 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001372 throw new UnsupportedOperationException
1373 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001374 }
1375}