blob: c63de62f8619532db4726f5f86c00e18f162739d [file] [log] [blame]
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080019import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080020import android.os.ParcelUuid;
21import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.util.Log;
23
24import java.util.ArrayList;
25import java.util.List;
26import java.util.UUID;
27
28/**
Matthew Xieddf7e472013-03-01 18:41:02 -080029 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080030 *
Matthew Xieddf7e472013-03-01 18:41:02 -080031 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080032 * with Bluetooth Smart or Smart Ready devices.
33 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080034 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070035 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080036 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
37 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080038 */
39public final class BluetoothGatt implements BluetoothProfile {
40 private static final String TAG = "BluetoothGatt";
41 private static final boolean DBG = true;
Matthew Xieddf7e472013-03-01 18:41:02 -080042 private static final boolean VDBG = true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080043
Matthew Xieddf7e472013-03-01 18:41:02 -080044 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045 private IBluetoothGatt mService;
46 private BluetoothGattCallback mCallback;
47 private int mClientIf;
48 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080049 private BluetoothDevice mDevice;
50 private boolean mAutoConnect;
51 private int mConnState;
52 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070053 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070054 private int mTransport;
Matthew Xieddf7e472013-03-01 18:41:02 -080055
56 private static final int CONN_STATE_IDLE = 0;
57 private static final int CONN_STATE_CONNECTING = 1;
58 private static final int CONN_STATE_CONNECTED = 2;
59 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070060 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080061
62 private List<BluetoothGattService> mServices;
63
Matthew Xieddf7e472013-03-01 18:41:02 -080064 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065 public static final int GATT_SUCCESS = 0;
66
Matthew Xieddf7e472013-03-01 18:41:02 -080067 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080068 public static final int GATT_READ_NOT_PERMITTED = 0x2;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
72
73 /** Insufficient authentication for a given operation */
74 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
75
76 /** The given request is not supported */
77 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
78
79 /** Insufficient encryption for a given operation */
80 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
81
82 /** A read or write operation was requested with an invalid offset */
83 public static final int GATT_INVALID_OFFSET = 0x7;
84
85 /** A write operation exceeds the maximum length of the attribute */
86 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
87
Andre Eisenbachdadefda2014-03-28 14:54:53 -070088 /** A remote device connection is congested. */
89 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
90
Matthew Xie90ca8072013-05-28 21:06:50 +000091 /** A GATT operation failed, errors other than the above */
92 public static final int GATT_FAILURE = 0x101;
93
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080094 /**
95 * No authentication required.
96 * @hide
97 */
98 /*package*/ static final int AUTHENTICATION_NONE = 0;
99
100 /**
101 * Authentication requested; no man-in-the-middle protection required.
102 * @hide
103 */
104 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
105
106 /**
107 * Authentication with man-in-the-middle protection requested.
108 * @hide
109 */
110 /*package*/ static final int AUTHENTICATION_MITM = 2;
111
112 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800113 * Bluetooth GATT interface callbacks
114 */
115 private final IBluetoothGattCallback mBluetoothGattCallback =
116 new IBluetoothGattCallback.Stub() {
117 /**
118 * Application interface registered - app is ready to go
119 * @hide
120 */
121 public void onClientRegistered(int status, int clientIf) {
122 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
123 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800124 if (VDBG) {
125 synchronized(mStateLock) {
126 if (mConnState != CONN_STATE_CONNECTING) {
127 Log.e(TAG, "Bad connection state: " + mConnState);
128 }
129 }
130 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800131 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800132 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700133 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800134 BluetoothProfile.STATE_DISCONNECTED);
135 synchronized(mStateLock) {
136 mConnState = CONN_STATE_IDLE;
137 }
138 return;
139 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800140 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800141 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700142 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800143 } catch (RemoteException e) {
144 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800145 }
146 }
147
148 /**
149 * Client connection state changed
150 * @hide
151 */
152 public void onClientConnectionState(int status, int clientIf,
153 boolean connected, String address) {
154 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
155 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800156 if (!address.equals(mDevice.getAddress())) {
157 return;
158 }
159 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
160 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800161 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700162 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800163 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700164 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800165 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800166
167 synchronized(mStateLock) {
168 if (connected) {
169 mConnState = CONN_STATE_CONNECTED;
170 } else {
171 mConnState = CONN_STATE_IDLE;
172 }
173 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700174
175 synchronized(mDeviceBusy) {
176 mDeviceBusy = false;
177 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800178 }
179
180 /**
181 * Callback reporting an LE scan result.
182 * @hide
183 */
184 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800185 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800186 }
187
188 /**
189 * A new GATT service has been discovered.
190 * The service is added to the internal list and the search
191 * continues.
192 * @hide
193 */
194 public void onGetService(String address, int srvcType,
195 int srvcInstId, ParcelUuid srvcUuid) {
196 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800197 if (!address.equals(mDevice.getAddress())) {
198 return;
199 }
200 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800201 srvcInstId, srvcType));
202 }
203
204 /**
205 * An included service has been found durig GATT discovery.
206 * The included service is added to the respective parent.
207 * @hide
208 */
209 public void onGetIncludedService(String address, int srvcType,
210 int srvcInstId, ParcelUuid srvcUuid,
211 int inclSrvcType, int inclSrvcInstId,
212 ParcelUuid inclSrvcUuid) {
213 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
214 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
215
Matthew Xieddf7e472013-03-01 18:41:02 -0800216 if (!address.equals(mDevice.getAddress())) {
217 return;
218 }
219 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800220 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800221 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800222 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
223
224 if (service != null && includedService != null) {
225 service.addIncludedService(includedService);
226 }
227 }
228
229 /**
230 * A new GATT characteristic has been discovered.
231 * Add the new characteristic to the relevant service and continue
232 * the remote device inspection.
233 * @hide
234 */
235 public void onGetCharacteristic(String address, int srvcType,
236 int srvcInstId, ParcelUuid srvcUuid,
237 int charInstId, ParcelUuid charUuid,
238 int charProps) {
239 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
240 charUuid);
241
Matthew Xieddf7e472013-03-01 18:41:02 -0800242 if (!address.equals(mDevice.getAddress())) {
243 return;
244 }
245 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800246 srvcInstId, srvcType);
247 if (service != null) {
248 service.addCharacteristic(new BluetoothGattCharacteristic(
249 service, charUuid.getUuid(), charInstId, charProps, 0));
250 }
251 }
252
253 /**
254 * A new GATT descriptor has been discovered.
255 * Finally, add the descriptor to the related characteristic.
256 * This should conclude the remote device update.
257 * @hide
258 */
259 public void onGetDescriptor(String address, int srvcType,
260 int srvcInstId, ParcelUuid srvcUuid,
261 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700262 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800263 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
264
Matthew Xieddf7e472013-03-01 18:41:02 -0800265 if (!address.equals(mDevice.getAddress())) {
266 return;
267 }
268 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800269 srvcInstId, srvcType);
270 if (service == null) return;
271
272 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
Mike J. Chen2975c682014-06-24 10:19:45 -0700273 charUuid.getUuid(), charInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800274 if (characteristic == null) return;
275
276 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700277 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800278 }
279
280 /**
281 * Remote search has been completed.
282 * The internal object structure should now reflect the state
283 * of the remote device database. Let the application know that
284 * we are done at this point.
285 * @hide
286 */
287 public void onSearchComplete(String address, int status) {
288 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800289 if (!address.equals(mDevice.getAddress())) {
290 return;
291 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800292 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700293 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800294 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700295 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800296 }
297 }
298
299 /**
300 * Remote characteristic has been read.
301 * Updates the internal value.
302 * @hide
303 */
304 public void onCharacteristicRead(String address, int status, int srvcType,
305 int srvcInstId, ParcelUuid srvcUuid,
306 int charInstId, ParcelUuid charUuid, byte[] value) {
307 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
308 + " UUID=" + charUuid + " Status=" + status);
309
Matthew Xieddf7e472013-03-01 18:41:02 -0800310 if (!address.equals(mDevice.getAddress())) {
311 return;
312 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700313
314 synchronized(mDeviceBusy) {
315 mDeviceBusy = false;
316 }
317
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800318 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
319 || status == GATT_INSUFFICIENT_ENCRYPTION)
320 && mAuthRetry == false) {
321 try {
322 mAuthRetry = true;
323 mService.readCharacteristic(mClientIf, address,
324 srvcType, srvcInstId, srvcUuid,
325 charInstId, charUuid, AUTHENTICATION_MITM);
326 return;
327 } catch (RemoteException e) {
328 Log.e(TAG,"",e);
329 }
330 }
331
332 mAuthRetry = false;
333
Matthew Xieddf7e472013-03-01 18:41:02 -0800334 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800335 srvcInstId, srvcType);
336 if (service == null) return;
337
338 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
339 charUuid.getUuid(), charInstId);
340 if (characteristic == null) return;
341
342 if (status == 0) characteristic.setValue(value);
343
344 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700345 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700347 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800348 }
349 }
350
351 /**
352 * Characteristic has been written to the remote device.
353 * Let the app know how we did...
354 * @hide
355 */
356 public void onCharacteristicWrite(String address, int status, int srvcType,
357 int srvcInstId, ParcelUuid srvcUuid,
358 int charInstId, ParcelUuid charUuid) {
359 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
360 + " UUID=" + charUuid + " Status=" + status);
361
Matthew Xieddf7e472013-03-01 18:41:02 -0800362 if (!address.equals(mDevice.getAddress())) {
363 return;
364 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700365
366 synchronized(mDeviceBusy) {
367 mDeviceBusy = false;
368 }
369
Matthew Xieddf7e472013-03-01 18:41:02 -0800370 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800371 srvcInstId, srvcType);
372 if (service == null) return;
373
374 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
375 charUuid.getUuid(), charInstId);
376 if (characteristic == null) return;
377
378 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
379 || status == GATT_INSUFFICIENT_ENCRYPTION)
380 && mAuthRetry == false) {
381 try {
382 mAuthRetry = true;
383 mService.writeCharacteristic(mClientIf, address,
384 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
385 characteristic.getWriteType(), AUTHENTICATION_MITM,
386 characteristic.getValue());
387 return;
388 } catch (RemoteException e) {
389 Log.e(TAG,"",e);
390 }
391 }
392
393 mAuthRetry = false;
394
395 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700396 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800397 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700398 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800399 }
400 }
401
402 /**
403 * Remote characteristic has been updated.
404 * Updates the internal value.
405 * @hide
406 */
407 public void onNotify(String address, int srvcType,
408 int srvcInstId, ParcelUuid srvcUuid,
409 int charInstId, ParcelUuid charUuid,
410 byte[] value) {
411 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
412
Matthew Xieddf7e472013-03-01 18:41:02 -0800413 if (!address.equals(mDevice.getAddress())) {
414 return;
415 }
416 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800417 srvcInstId, srvcType);
418 if (service == null) return;
419
420 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
421 charUuid.getUuid(), charInstId);
422 if (characteristic == null) return;
423
424 characteristic.setValue(value);
425
426 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700427 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800428 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700429 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800430 }
431 }
432
433 /**
434 * Descriptor has been read.
435 * @hide
436 */
437 public void onDescriptorRead(String address, int status, int srvcType,
438 int srvcInstId, ParcelUuid srvcUuid,
439 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700440 int descrInstId, ParcelUuid descrUuid,
441 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800442 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
443
Matthew Xieddf7e472013-03-01 18:41:02 -0800444 if (!address.equals(mDevice.getAddress())) {
445 return;
446 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700447
448 synchronized(mDeviceBusy) {
449 mDeviceBusy = false;
450 }
451
Matthew Xieddf7e472013-03-01 18:41:02 -0800452 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800453 srvcInstId, srvcType);
454 if (service == null) return;
455
456 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
457 charUuid.getUuid(), charInstId);
458 if (characteristic == null) return;
459
460 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700461 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800462 if (descriptor == null) return;
463
464 if (status == 0) descriptor.setValue(value);
465
466 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
467 || status == GATT_INSUFFICIENT_ENCRYPTION)
468 && mAuthRetry == false) {
469 try {
470 mAuthRetry = true;
471 mService.readDescriptor(mClientIf, address,
472 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700473 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800474 } catch (RemoteException e) {
475 Log.e(TAG,"",e);
476 }
477 }
478
479 mAuthRetry = true;
480
481 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700482 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800483 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700484 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800485 }
486 }
487
488 /**
489 * Descriptor write operation complete.
490 * @hide
491 */
492 public void onDescriptorWrite(String address, int status, int srvcType,
493 int srvcInstId, ParcelUuid srvcUuid,
494 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700495 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800496 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
497
Matthew Xieddf7e472013-03-01 18:41:02 -0800498 if (!address.equals(mDevice.getAddress())) {
499 return;
500 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700501
502 synchronized(mDeviceBusy) {
503 mDeviceBusy = false;
504 }
505
Matthew Xieddf7e472013-03-01 18:41:02 -0800506 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800507 srvcInstId, srvcType);
508 if (service == null) return;
509
510 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
511 charUuid.getUuid(), charInstId);
512 if (characteristic == null) return;
513
514 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700515 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800516 if (descriptor == null) return;
517
518 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
519 || status == GATT_INSUFFICIENT_ENCRYPTION)
520 && mAuthRetry == false) {
521 try {
522 mAuthRetry = true;
523 mService.writeDescriptor(mClientIf, address,
524 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700525 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800526 AUTHENTICATION_MITM, descriptor.getValue());
527 } catch (RemoteException e) {
528 Log.e(TAG,"",e);
529 }
530 }
531
532 mAuthRetry = false;
533
534 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700535 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800536 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700537 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800538 }
539 }
540
541 /**
542 * Prepared write transaction completed (or aborted)
543 * @hide
544 */
545 public void onExecuteWrite(String address, int status) {
546 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
547 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800548 if (!address.equals(mDevice.getAddress())) {
549 return;
550 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700551
552 synchronized(mDeviceBusy) {
553 mDeviceBusy = false;
554 }
555
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800556 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700557 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800558 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700559 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800560 }
561 }
562
563 /**
564 * Remote device RSSI has been read
565 * @hide
566 */
567 public void onReadRemoteRssi(String address, int rssi, int status) {
568 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
569 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800570 if (!address.equals(mDevice.getAddress())) {
571 return;
572 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800573 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700574 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800575 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700576 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800577 }
578 }
Wei Wangf3055892014-03-11 22:22:41 -0700579
580 /**
581 * Advertise state change callback
582 * @hide
583 */
584 public void onAdvertiseStateChange(int state, int status) {
585 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
586 + state + " status=" + status);
Wei Wangadf6aff2014-05-20 06:30:20 +0000587 }
588
589 /**
590 * @hide
591 */
592 @Override
593 public void onMultiAdvertiseCallback(int status) {
594 // no op.
595 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700596
597 /**
598 * Callback invoked when the MTU for a given connection changes
599 * @hide
600 */
601 public void onConfigureMTU(String address, int mtu, int status) {
602 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
603 " mtu=" + mtu + " status=" + status);
604 if (!address.equals(mDevice.getAddress())) {
605 return;
606 }
607 try {
608 mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status);
609 } catch (Exception ex) {
610 Log.w(TAG, "Unhandled exception in callback", ex);
611 }
Wei Wangf3055892014-03-11 22:22:41 -0700612 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700613
614 /**
615 * Callback indicating the remote device connection is congested.
616 * @hide
617 */
618 public void onConnectionCongested(String address, boolean congested) {
619 if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address
620 + " congested=" + congested);
621 if (!address.equals(mDevice.getAddress())) return;
622 try {
623 mCallback.onConnectionCongested(BluetoothGatt.this, congested);
624 } catch (Exception ex) {
625 Log.w(TAG, "Unhandled exception in callback", ex);
626 }
627 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800628 };
629
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700630 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
631 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800632 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800633 mService = iGatt;
634 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700635 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800636 mServices = new ArrayList<BluetoothGattService>();
637
Matthew Xieddf7e472013-03-01 18:41:02 -0800638 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800639 }
640
641 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700642 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700643 *
644 * Application should call this method as early as possible after it is done with
645 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800646 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700647 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800648 if (DBG) Log.d(TAG, "close()");
649
650 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700651 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800652 }
653
654 /**
655 * Returns a service by UUID, instance and type.
656 * @hide
657 */
658 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
659 int instanceId, int type) {
660 for(BluetoothGattService svc : mServices) {
661 if (svc.getDevice().equals(device) &&
662 svc.getType() == type &&
663 svc.getInstanceId() == instanceId &&
664 svc.getUuid().equals(uuid)) {
665 return svc;
666 }
667 }
668 return null;
669 }
670
671
672 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800673 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800674 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800675 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
676 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800677 *
678 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
679 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800680 * @param callback GATT callback handler that will receive asynchronous callbacks.
681 * @return If true, the callback will be called to notify success or failure,
682 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800683 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800684 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800685 if (DBG) Log.d(TAG, "registerApp()");
686 if (mService == null) return false;
687
688 mCallback = callback;
689 UUID uuid = UUID.randomUUID();
690 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
691
692 try {
693 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
694 } catch (RemoteException e) {
695 Log.e(TAG,"",e);
696 return false;
697 }
698
699 return true;
700 }
701
702 /**
703 * Unregister the current application and callbacks.
704 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800705 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800706 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
707 if (mService == null || mClientIf == 0) return;
708
709 try {
710 mCallback = null;
711 mService.unregisterClient(mClientIf);
712 mClientIf = 0;
713 } catch (RemoteException e) {
714 Log.e(TAG,"",e);
715 }
716 }
717
718 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800719 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800720 *
721 * <p>The connection may not be established right away, but will be
722 * completed when the remote device is available. A
723 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
724 * invoked when the connection state changes as a result of this function.
725 *
726 * <p>The autoConnect paramter determines whether to actively connect to
727 * the remote device, or rather passively scan and finalize the connection
728 * when the remote device is in range/available. Generally, the first ever
729 * connection to a device should be direct (autoConnect set to false) and
730 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800731 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800732 *
733 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
734 *
735 * @param device Remote device to connect to
736 * @param autoConnect Whether to directly connect to the remote device (false)
737 * or to automatically connect as soon as the remote
738 * device becomes available (true).
739 * @return true, if the connection attempt was initiated successfully
740 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800741 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
742 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
743 synchronized(mStateLock) {
744 if (mConnState != CONN_STATE_IDLE) {
745 throw new IllegalStateException("Not idle");
746 }
747 mConnState = CONN_STATE_CONNECTING;
748 }
749 if (!registerApp(callback)) {
750 synchronized(mStateLock) {
751 mConnState = CONN_STATE_IDLE;
752 }
753 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800754 return false;
755 }
756
Matthew Xieddf7e472013-03-01 18:41:02 -0800757 // the connection will continue after successful callback registration
758 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800759 return true;
760 }
761
762 /**
763 * Disconnects an established connection, or cancels a connection attempt
764 * currently in progress.
765 *
766 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800767 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800768 public void disconnect() {
769 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800770 if (mService == null || mClientIf == 0) return;
771
772 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800773 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800774 } catch (RemoteException e) {
775 Log.e(TAG,"",e);
776 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700777 }
778
779 /**
780 * Connect back to remote device.
781 *
782 * <p>This method is used to re-connect to a remote device after the
783 * connection has been dropped. If the device is not in range, the
784 * re-connection will be triggered once the device is back in range.
785 *
786 * @return true, if the connection attempt was initiated successfully
787 */
788 public boolean connect() {
789 try {
790 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700791 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700792 return true;
793 } catch (RemoteException e) {
794 Log.e(TAG,"",e);
795 return false;
796 }
797 }
798
799 /**
800 * Return the remote bluetooth device this GATT client targets to
801 *
802 * @return remote bluetooth device
803 */
804 public BluetoothDevice getDevice() {
805 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 }
807
808 /**
809 * Discovers services offered by a remote device as well as their
810 * characteristics and descriptors.
811 *
812 * <p>This is an asynchronous operation. Once service discovery is completed,
813 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
814 * triggered. If the discovery was successful, the remote services can be
815 * retrieved using the {@link #getServices} function.
816 *
817 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
818 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800819 * @return true, if the remote service discovery has been started
820 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800821 public boolean discoverServices() {
822 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800823 if (mService == null || mClientIf == 0) return false;
824
825 mServices.clear();
826
827 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800828 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800829 } catch (RemoteException e) {
830 Log.e(TAG,"",e);
831 return false;
832 }
833
834 return true;
835 }
836
837 /**
838 * Returns a list of GATT services offered by the remote device.
839 *
840 * <p>This function requires that service discovery has been completed
841 * for the given device.
842 *
843 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
844 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800845 * @return List of services on the remote device. Returns an empty list
846 * if service discovery has not yet been performed.
847 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800848 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800849 List<BluetoothGattService> result =
850 new ArrayList<BluetoothGattService>();
851
852 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800853 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800854 result.add(service);
855 }
856 }
857
858 return result;
859 }
860
861 /**
862 * Returns a {@link BluetoothGattService}, if the requested UUID is
863 * supported by the remote device.
864 *
865 * <p>This function requires that service discovery has been completed
866 * for the given device.
867 *
868 * <p>If multiple instances of the same service (as identified by UUID)
869 * exist, the first instance of the service is returned.
870 *
871 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
872 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800873 * @param uuid UUID of the requested service
874 * @return BluetoothGattService if supported, or null if the requested
875 * service is not offered by the remote device.
876 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800877 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800878 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800879 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800880 service.getUuid().equals(uuid)) {
881 return service;
882 }
883 }
884
885 return null;
886 }
887
888 /**
889 * Reads the requested characteristic from the associated remote device.
890 *
891 * <p>This is an asynchronous operation. The result of the read operation
892 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
893 * callback.
894 *
895 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
896 *
897 * @param characteristic Characteristic to read from the remote device
898 * @return true, if the read operation was initiated successfully
899 */
900 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
901 if ((characteristic.getProperties() &
902 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
903
904 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
905 if (mService == null || mClientIf == 0) return false;
906
907 BluetoothGattService service = characteristic.getService();
908 if (service == null) return false;
909
910 BluetoothDevice device = service.getDevice();
911 if (device == null) return false;
912
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700913 synchronized(mDeviceBusy) {
914 if (mDeviceBusy) return false;
915 mDeviceBusy = true;
916 }
917
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800918 try {
919 mService.readCharacteristic(mClientIf, device.getAddress(),
920 service.getType(), service.getInstanceId(),
921 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
922 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
923 } catch (RemoteException e) {
924 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700925 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800926 return false;
927 }
928
929 return true;
930 }
931
932 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800933 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800934 *
935 * <p>Once the write operation has been completed, the
936 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
937 * reporting the result of the operation.
938 *
939 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
940 *
941 * @param characteristic Characteristic to write on the remote device
942 * @return true, if the write operation was initiated successfully
943 */
944 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
945 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
946 && (characteristic.getProperties() &
947 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
948
949 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
950 if (mService == null || mClientIf == 0) return false;
951
952 BluetoothGattService service = characteristic.getService();
953 if (service == null) return false;
954
955 BluetoothDevice device = service.getDevice();
956 if (device == null) return false;
957
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700958 synchronized(mDeviceBusy) {
959 if (mDeviceBusy) return false;
960 mDeviceBusy = true;
961 }
962
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800963 try {
964 mService.writeCharacteristic(mClientIf, device.getAddress(),
965 service.getType(), service.getInstanceId(),
966 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
967 new ParcelUuid(characteristic.getUuid()),
968 characteristic.getWriteType(), AUTHENTICATION_NONE,
969 characteristic.getValue());
970 } catch (RemoteException e) {
971 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700972 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800973 return false;
974 }
975
976 return true;
977 }
978
979 /**
980 * Reads the value for a given descriptor from the associated remote device.
981 *
982 * <p>Once the read operation has been completed, the
983 * {@link BluetoothGattCallback#onDescriptorRead} callback is
984 * triggered, signaling the result of the operation.
985 *
986 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
987 *
988 * @param descriptor Descriptor value to read from the remote device
989 * @return true, if the read operation was initiated successfully
990 */
991 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
992 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
993 if (mService == null || mClientIf == 0) return false;
994
995 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
996 if (characteristic == null) return false;
997
998 BluetoothGattService service = characteristic.getService();
999 if (service == null) return false;
1000
1001 BluetoothDevice device = service.getDevice();
1002 if (device == null) return false;
1003
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001004 synchronized(mDeviceBusy) {
1005 if (mDeviceBusy) return false;
1006 mDeviceBusy = true;
1007 }
1008
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001009 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001010 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
1011 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1012 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1013 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
1014 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 } catch (RemoteException e) {
1016 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001017 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001018 return false;
1019 }
1020
1021 return true;
1022 }
1023
1024 /**
1025 * Write the value of a given descriptor to the associated remote device.
1026 *
1027 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1028 * triggered to report the result of the write operation.
1029 *
1030 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1031 *
1032 * @param descriptor Descriptor to write to the associated remote device
1033 * @return true, if the write operation was initiated successfully
1034 */
1035 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1036 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1037 if (mService == null || mClientIf == 0) return false;
1038
1039 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1040 if (characteristic == null) return false;
1041
1042 BluetoothGattService service = characteristic.getService();
1043 if (service == null) return false;
1044
1045 BluetoothDevice device = service.getDevice();
1046 if (device == null) return false;
1047
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001048 synchronized(mDeviceBusy) {
1049 if (mDeviceBusy) return false;
1050 mDeviceBusy = true;
1051 }
1052
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001053 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001054 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1055 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1056 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1057 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001058 characteristic.getWriteType(), AUTHENTICATION_NONE,
1059 descriptor.getValue());
1060 } catch (RemoteException e) {
1061 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001062 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001063 return false;
1064 }
1065
1066 return true;
1067 }
1068
1069 /**
1070 * Initiates a reliable write transaction for a given remote device.
1071 *
1072 * <p>Once a reliable write transaction has been initiated, all calls
1073 * to {@link #writeCharacteristic} are sent to the remote device for
1074 * verification and queued up for atomic execution. The application will
1075 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1076 * in response to every {@link #writeCharacteristic} call and is responsible
1077 * for verifying if the value has been transmitted accurately.
1078 *
1079 * <p>After all characteristics have been queued up and verified,
1080 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1081 * was not written correctly, calling {@link #abortReliableWrite} will
1082 * cancel the current transaction without commiting any values on the
1083 * remote device.
1084 *
1085 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1086 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001087 * @return true, if the reliable write transaction has been initiated
1088 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001089 public boolean beginReliableWrite() {
1090 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001091 if (mService == null || mClientIf == 0) return false;
1092
1093 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001094 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001095 } catch (RemoteException e) {
1096 Log.e(TAG,"",e);
1097 return false;
1098 }
1099
1100 return true;
1101 }
1102
1103 /**
1104 * Executes a reliable write transaction for a given remote device.
1105 *
1106 * <p>This function will commit all queued up characteristic write
1107 * operations for a given remote device.
1108 *
1109 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1110 * invoked to indicate whether the transaction has been executed correctly.
1111 *
1112 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1113 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001114 * @return true, if the request to execute the transaction has been sent
1115 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001116 public boolean executeReliableWrite() {
1117 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001118 if (mService == null || mClientIf == 0) return false;
1119
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001120 synchronized(mDeviceBusy) {
1121 if (mDeviceBusy) return false;
1122 mDeviceBusy = true;
1123 }
1124
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001125 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001126 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001127 } catch (RemoteException e) {
1128 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001129 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001130 return false;
1131 }
1132
1133 return true;
1134 }
1135
1136 /**
1137 * Cancels a reliable write transaction for a given device.
1138 *
1139 * <p>Calling this function will discard all queued characteristic write
1140 * operations for a given remote device.
1141 *
1142 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001143 */
John Du48f8b5d2013-08-19 12:20:37 -07001144 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001145 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001146 if (mService == null || mClientIf == 0) return;
1147
1148 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001149 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001150 } catch (RemoteException e) {
1151 Log.e(TAG,"",e);
1152 }
1153 }
1154
1155 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001156 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001157 */
1158 public void abortReliableWrite(BluetoothDevice mDevice) {
1159 abortReliableWrite();
1160 }
1161
1162 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001163 * Enable or disable notifications/indications for a given characteristic.
1164 *
1165 * <p>Once notifications are enabled for a characteristic, a
1166 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1167 * triggered if the remote device indicates that the given characteristic
1168 * has changed.
1169 *
1170 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1171 *
1172 * @param characteristic The characteristic for which to enable notifications
1173 * @param enable Set to true to enable notifications/indications
1174 * @return true, if the requested notification status was set successfully
1175 */
1176 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1177 boolean enable) {
1178 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1179 + " enable: " + enable);
1180 if (mService == null || mClientIf == 0) return false;
1181
1182 BluetoothGattService service = characteristic.getService();
1183 if (service == null) return false;
1184
1185 BluetoothDevice device = service.getDevice();
1186 if (device == null) return false;
1187
1188 try {
1189 mService.registerForNotification(mClientIf, device.getAddress(),
1190 service.getType(), service.getInstanceId(),
1191 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1192 new ParcelUuid(characteristic.getUuid()),
1193 enable);
1194 } catch (RemoteException e) {
1195 Log.e(TAG,"",e);
1196 return false;
1197 }
1198
1199 return true;
1200 }
1201
1202 /**
1203 * Clears the internal cache and forces a refresh of the services from the
1204 * remote device.
1205 * @hide
1206 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001207 public boolean refresh() {
1208 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001209 if (mService == null || mClientIf == 0) return false;
1210
1211 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001212 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001213 } catch (RemoteException e) {
1214 Log.e(TAG,"",e);
1215 return false;
1216 }
1217
1218 return true;
1219 }
1220
1221 /**
1222 * Read the RSSI for a connected remote device.
1223 *
1224 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1225 * invoked when the RSSI value has been read.
1226 *
1227 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1228 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001229 * @return true, if the RSSI value has been requested successfully
1230 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001231 public boolean readRemoteRssi() {
1232 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 if (mService == null || mClientIf == 0) return false;
1234
1235 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001236 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001237 } catch (RemoteException e) {
1238 Log.e(TAG,"",e);
1239 return false;
1240 }
1241
1242 return true;
1243 }
1244
1245 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001246 * Configure the MTU used for a given connection.
1247 *
1248 * <p>When performing a write request operation (write without response),
1249 * the data sent is truncated to the MTU size. This function may be used
1250 * to request a larget MTU size to be able to send more data at once.
1251 *
1252 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1253 * whether this operation was successful.
1254 *
1255 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1256 *
1257 * @return true, if the new MTU value has been requested successfully
1258 * @hide
1259 */
1260 public boolean configureMTU(int mtu) {
1261 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1262 + " mtu: " + mtu);
1263 if (mService == null || mClientIf == 0) return false;
1264
1265 try {
1266 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1267 } catch (RemoteException e) {
1268 Log.e(TAG,"",e);
1269 return false;
1270 }
1271
1272 return true;
1273 }
1274
1275 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001276 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1277 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001278 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001279 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001280 */
1281 @Override
1282 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001283 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001284 }
1285
1286 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001287 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1288 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001289 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001290 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001291 */
1292 @Override
1293 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001294 throw new UnsupportedOperationException
1295 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001296 }
1297
1298 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001299 * Not supported - please use
1300 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1301 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001302 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001303 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001304 */
1305 @Override
1306 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001307 throw new UnsupportedOperationException
1308 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001309 }
1310}