blob: 601d9eee87340696914d747c692fdd058669f0f0 [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
Matthew Xie90ca8072013-05-28 21:06:50 +000088 /** A GATT operation failed, errors other than the above */
89 public static final int GATT_FAILURE = 0x101;
90
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080091 /**
92 * No authentication required.
93 * @hide
94 */
95 /*package*/ static final int AUTHENTICATION_NONE = 0;
96
97 /**
98 * Authentication requested; no man-in-the-middle protection required.
99 * @hide
100 */
101 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
102
103 /**
104 * Authentication with man-in-the-middle protection requested.
105 * @hide
106 */
107 /*package*/ static final int AUTHENTICATION_MITM = 2;
108
109 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800110 * Bluetooth GATT interface callbacks
111 */
112 private final IBluetoothGattCallback mBluetoothGattCallback =
113 new IBluetoothGattCallback.Stub() {
114 /**
115 * Application interface registered - app is ready to go
116 * @hide
117 */
118 public void onClientRegistered(int status, int clientIf) {
119 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
120 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800121 if (VDBG) {
122 synchronized(mStateLock) {
123 if (mConnState != CONN_STATE_CONNECTING) {
124 Log.e(TAG, "Bad connection state: " + mConnState);
125 }
126 }
127 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800128 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800129 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700130 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800131 BluetoothProfile.STATE_DISCONNECTED);
132 synchronized(mStateLock) {
133 mConnState = CONN_STATE_IDLE;
134 }
135 return;
136 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800138 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700139 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800140 } catch (RemoteException e) {
141 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800142 }
143 }
144
145 /**
146 * Client connection state changed
147 * @hide
148 */
149 public void onClientConnectionState(int status, int clientIf,
150 boolean connected, String address) {
151 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
152 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800153 if (!address.equals(mDevice.getAddress())) {
154 return;
155 }
156 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
157 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800158 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700159 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800160 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700161 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800162 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800163
164 synchronized(mStateLock) {
165 if (connected) {
166 mConnState = CONN_STATE_CONNECTED;
167 } else {
168 mConnState = CONN_STATE_IDLE;
169 }
170 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700171
172 synchronized(mDeviceBusy) {
173 mDeviceBusy = false;
174 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800175 }
176
177 /**
178 * Callback reporting an LE scan result.
179 * @hide
180 */
181 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800182 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800183 }
184
185 /**
186 * A new GATT service has been discovered.
187 * The service is added to the internal list and the search
188 * continues.
189 * @hide
190 */
191 public void onGetService(String address, int srvcType,
192 int srvcInstId, ParcelUuid srvcUuid) {
193 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800194 if (!address.equals(mDevice.getAddress())) {
195 return;
196 }
197 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800198 srvcInstId, srvcType));
199 }
200
201 /**
202 * An included service has been found durig GATT discovery.
203 * The included service is added to the respective parent.
204 * @hide
205 */
206 public void onGetIncludedService(String address, int srvcType,
207 int srvcInstId, ParcelUuid srvcUuid,
208 int inclSrvcType, int inclSrvcInstId,
209 ParcelUuid inclSrvcUuid) {
210 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
211 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
212
Matthew Xieddf7e472013-03-01 18:41:02 -0800213 if (!address.equals(mDevice.getAddress())) {
214 return;
215 }
216 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800217 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800218 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800219 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
220
221 if (service != null && includedService != null) {
222 service.addIncludedService(includedService);
223 }
224 }
225
226 /**
227 * A new GATT characteristic has been discovered.
228 * Add the new characteristic to the relevant service and continue
229 * the remote device inspection.
230 * @hide
231 */
232 public void onGetCharacteristic(String address, int srvcType,
233 int srvcInstId, ParcelUuid srvcUuid,
234 int charInstId, ParcelUuid charUuid,
235 int charProps) {
236 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
237 charUuid);
238
Matthew Xieddf7e472013-03-01 18:41:02 -0800239 if (!address.equals(mDevice.getAddress())) {
240 return;
241 }
242 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800243 srvcInstId, srvcType);
244 if (service != null) {
245 service.addCharacteristic(new BluetoothGattCharacteristic(
246 service, charUuid.getUuid(), charInstId, charProps, 0));
247 }
248 }
249
250 /**
251 * A new GATT descriptor has been discovered.
252 * Finally, add the descriptor to the related characteristic.
253 * This should conclude the remote device update.
254 * @hide
255 */
256 public void onGetDescriptor(String address, int srvcType,
257 int srvcInstId, ParcelUuid srvcUuid,
258 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700259 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800260 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
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) return;
268
269 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
270 charUuid.getUuid());
271 if (characteristic == null) return;
272
273 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700274 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800275 }
276
277 /**
278 * Remote search has been completed.
279 * The internal object structure should now reflect the state
280 * of the remote device database. Let the application know that
281 * we are done at this point.
282 * @hide
283 */
284 public void onSearchComplete(String address, int status) {
285 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800286 if (!address.equals(mDevice.getAddress())) {
287 return;
288 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800289 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700290 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800291 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700292 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800293 }
294 }
295
296 /**
297 * Remote characteristic has been read.
298 * Updates the internal value.
299 * @hide
300 */
301 public void onCharacteristicRead(String address, int status, int srvcType,
302 int srvcInstId, ParcelUuid srvcUuid,
303 int charInstId, ParcelUuid charUuid, byte[] value) {
304 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
305 + " UUID=" + charUuid + " Status=" + status);
306
Matthew Xieddf7e472013-03-01 18:41:02 -0800307 if (!address.equals(mDevice.getAddress())) {
308 return;
309 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700310
311 synchronized(mDeviceBusy) {
312 mDeviceBusy = false;
313 }
314
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800315 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
316 || status == GATT_INSUFFICIENT_ENCRYPTION)
317 && mAuthRetry == false) {
318 try {
319 mAuthRetry = true;
320 mService.readCharacteristic(mClientIf, address,
321 srvcType, srvcInstId, srvcUuid,
322 charInstId, charUuid, AUTHENTICATION_MITM);
323 return;
324 } catch (RemoteException e) {
325 Log.e(TAG,"",e);
326 }
327 }
328
329 mAuthRetry = false;
330
Matthew Xieddf7e472013-03-01 18:41:02 -0800331 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800332 srvcInstId, srvcType);
333 if (service == null) return;
334
335 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
336 charUuid.getUuid(), charInstId);
337 if (characteristic == null) return;
338
339 if (status == 0) characteristic.setValue(value);
340
341 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700342 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800343 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700344 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800345 }
346 }
347
348 /**
349 * Characteristic has been written to the remote device.
350 * Let the app know how we did...
351 * @hide
352 */
353 public void onCharacteristicWrite(String address, int status, int srvcType,
354 int srvcInstId, ParcelUuid srvcUuid,
355 int charInstId, ParcelUuid charUuid) {
356 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
357 + " UUID=" + charUuid + " Status=" + status);
358
Matthew Xieddf7e472013-03-01 18:41:02 -0800359 if (!address.equals(mDevice.getAddress())) {
360 return;
361 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700362
363 synchronized(mDeviceBusy) {
364 mDeviceBusy = false;
365 }
366
Matthew Xieddf7e472013-03-01 18:41:02 -0800367 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800368 srvcInstId, srvcType);
369 if (service == null) return;
370
371 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
372 charUuid.getUuid(), charInstId);
373 if (characteristic == null) return;
374
375 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
376 || status == GATT_INSUFFICIENT_ENCRYPTION)
377 && mAuthRetry == false) {
378 try {
379 mAuthRetry = true;
380 mService.writeCharacteristic(mClientIf, address,
381 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
382 characteristic.getWriteType(), AUTHENTICATION_MITM,
383 characteristic.getValue());
384 return;
385 } catch (RemoteException e) {
386 Log.e(TAG,"",e);
387 }
388 }
389
390 mAuthRetry = false;
391
392 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700393 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800394 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700395 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800396 }
397 }
398
399 /**
400 * Remote characteristic has been updated.
401 * Updates the internal value.
402 * @hide
403 */
404 public void onNotify(String address, int srvcType,
405 int srvcInstId, ParcelUuid srvcUuid,
406 int charInstId, ParcelUuid charUuid,
407 byte[] value) {
408 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
409
Matthew Xieddf7e472013-03-01 18:41:02 -0800410 if (!address.equals(mDevice.getAddress())) {
411 return;
412 }
413 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800414 srvcInstId, srvcType);
415 if (service == null) return;
416
417 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
418 charUuid.getUuid(), charInstId);
419 if (characteristic == null) return;
420
421 characteristic.setValue(value);
422
423 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700424 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800425 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700426 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800427 }
428 }
429
430 /**
431 * Descriptor has been read.
432 * @hide
433 */
434 public void onDescriptorRead(String address, int status, int srvcType,
435 int srvcInstId, ParcelUuid srvcUuid,
436 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700437 int descrInstId, ParcelUuid descrUuid,
438 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800439 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
440
Matthew Xieddf7e472013-03-01 18:41:02 -0800441 if (!address.equals(mDevice.getAddress())) {
442 return;
443 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700444
445 synchronized(mDeviceBusy) {
446 mDeviceBusy = false;
447 }
448
Matthew Xieddf7e472013-03-01 18:41:02 -0800449 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800450 srvcInstId, srvcType);
451 if (service == null) return;
452
453 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
454 charUuid.getUuid(), charInstId);
455 if (characteristic == null) return;
456
457 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700458 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800459 if (descriptor == null) return;
460
461 if (status == 0) descriptor.setValue(value);
462
463 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
464 || status == GATT_INSUFFICIENT_ENCRYPTION)
465 && mAuthRetry == false) {
466 try {
467 mAuthRetry = true;
468 mService.readDescriptor(mClientIf, address,
469 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700470 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800471 } catch (RemoteException e) {
472 Log.e(TAG,"",e);
473 }
474 }
475
476 mAuthRetry = true;
477
478 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700479 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800480 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700481 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800482 }
483 }
484
485 /**
486 * Descriptor write operation complete.
487 * @hide
488 */
489 public void onDescriptorWrite(String address, int status, int srvcType,
490 int srvcInstId, ParcelUuid srvcUuid,
491 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700492 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800493 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
494
Matthew Xieddf7e472013-03-01 18:41:02 -0800495 if (!address.equals(mDevice.getAddress())) {
496 return;
497 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700498
499 synchronized(mDeviceBusy) {
500 mDeviceBusy = false;
501 }
502
Matthew Xieddf7e472013-03-01 18:41:02 -0800503 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504 srvcInstId, srvcType);
505 if (service == null) return;
506
507 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
508 charUuid.getUuid(), charInstId);
509 if (characteristic == null) return;
510
511 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700512 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800513 if (descriptor == null) return;
514
515 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
516 || status == GATT_INSUFFICIENT_ENCRYPTION)
517 && mAuthRetry == false) {
518 try {
519 mAuthRetry = true;
520 mService.writeDescriptor(mClientIf, address,
521 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700522 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 AUTHENTICATION_MITM, descriptor.getValue());
524 } catch (RemoteException e) {
525 Log.e(TAG,"",e);
526 }
527 }
528
529 mAuthRetry = false;
530
531 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700532 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800533 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700534 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800535 }
536 }
537
538 /**
539 * Prepared write transaction completed (or aborted)
540 * @hide
541 */
542 public void onExecuteWrite(String address, int status) {
543 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
544 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800545 if (!address.equals(mDevice.getAddress())) {
546 return;
547 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700548
549 synchronized(mDeviceBusy) {
550 mDeviceBusy = false;
551 }
552
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800553 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700554 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800555 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700556 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800557 }
558 }
559
560 /**
561 * Remote device RSSI has been read
562 * @hide
563 */
564 public void onReadRemoteRssi(String address, int rssi, int status) {
565 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
566 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800567 if (!address.equals(mDevice.getAddress())) {
568 return;
569 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800570 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700571 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800572 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700573 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800574 }
575 }
Wei Wangf3055892014-03-11 22:22:41 -0700576
577 /**
578 * Advertise state change callback
579 * @hide
580 */
581 public void onAdvertiseStateChange(int state, int status) {
582 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
583 + state + " status=" + status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700584 }
585
586 /**
587 * Callback invoked when the MTU for a given connection changes
588 * @hide
589 */
590 public void onConfigureMTU(String address, int mtu, int status) {
591 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
592 " mtu=" + mtu + " status=" + status);
593 if (!address.equals(mDevice.getAddress())) {
594 return;
595 }
596 try {
597 mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status);
598 } catch (Exception ex) {
599 Log.w(TAG, "Unhandled exception in callback", ex);
600 }
Wei Wangf3055892014-03-11 22:22:41 -0700601 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800602 };
603
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700604 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
605 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800606 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800607 mService = iGatt;
608 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700609 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800610 mServices = new ArrayList<BluetoothGattService>();
611
Matthew Xieddf7e472013-03-01 18:41:02 -0800612 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800613 }
614
615 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700616 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700617 *
618 * Application should call this method as early as possible after it is done with
619 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800620 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700621 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800622 if (DBG) Log.d(TAG, "close()");
623
624 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700625 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800626 }
627
628 /**
629 * Returns a service by UUID, instance and type.
630 * @hide
631 */
632 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
633 int instanceId, int type) {
634 for(BluetoothGattService svc : mServices) {
635 if (svc.getDevice().equals(device) &&
636 svc.getType() == type &&
637 svc.getInstanceId() == instanceId &&
638 svc.getUuid().equals(uuid)) {
639 return svc;
640 }
641 }
642 return null;
643 }
644
645
646 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800647 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800648 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800649 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
650 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800651 *
652 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
653 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800654 * @param callback GATT callback handler that will receive asynchronous callbacks.
655 * @return If true, the callback will be called to notify success or failure,
656 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800657 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800658 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800659 if (DBG) Log.d(TAG, "registerApp()");
660 if (mService == null) return false;
661
662 mCallback = callback;
663 UUID uuid = UUID.randomUUID();
664 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
665
666 try {
667 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
668 } catch (RemoteException e) {
669 Log.e(TAG,"",e);
670 return false;
671 }
672
673 return true;
674 }
675
676 /**
677 * Unregister the current application and callbacks.
678 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800679 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800680 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
681 if (mService == null || mClientIf == 0) return;
682
683 try {
684 mCallback = null;
685 mService.unregisterClient(mClientIf);
686 mClientIf = 0;
687 } catch (RemoteException e) {
688 Log.e(TAG,"",e);
689 }
690 }
691
692 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800693 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800694 *
695 * <p>The connection may not be established right away, but will be
696 * completed when the remote device is available. A
697 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
698 * invoked when the connection state changes as a result of this function.
699 *
700 * <p>The autoConnect paramter determines whether to actively connect to
701 * the remote device, or rather passively scan and finalize the connection
702 * when the remote device is in range/available. Generally, the first ever
703 * connection to a device should be direct (autoConnect set to false) and
704 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800705 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800706 *
707 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
708 *
709 * @param device Remote device to connect to
710 * @param autoConnect Whether to directly connect to the remote device (false)
711 * or to automatically connect as soon as the remote
712 * device becomes available (true).
713 * @return true, if the connection attempt was initiated successfully
714 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800715 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
716 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
717 synchronized(mStateLock) {
718 if (mConnState != CONN_STATE_IDLE) {
719 throw new IllegalStateException("Not idle");
720 }
721 mConnState = CONN_STATE_CONNECTING;
722 }
723 if (!registerApp(callback)) {
724 synchronized(mStateLock) {
725 mConnState = CONN_STATE_IDLE;
726 }
727 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800728 return false;
729 }
730
Matthew Xieddf7e472013-03-01 18:41:02 -0800731 // the connection will continue after successful callback registration
732 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800733 return true;
734 }
735
736 /**
737 * Disconnects an established connection, or cancels a connection attempt
738 * currently in progress.
739 *
740 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800741 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800742 public void disconnect() {
743 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800744 if (mService == null || mClientIf == 0) return;
745
746 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800747 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800748 } catch (RemoteException e) {
749 Log.e(TAG,"",e);
750 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700751 }
752
753 /**
754 * Connect back to remote device.
755 *
756 * <p>This method is used to re-connect to a remote device after the
757 * connection has been dropped. If the device is not in range, the
758 * re-connection will be triggered once the device is back in range.
759 *
760 * @return true, if the connection attempt was initiated successfully
761 */
762 public boolean connect() {
763 try {
764 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700765 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700766 return true;
767 } catch (RemoteException e) {
768 Log.e(TAG,"",e);
769 return false;
770 }
771 }
772
773 /**
774 * Return the remote bluetooth device this GATT client targets to
775 *
776 * @return remote bluetooth device
777 */
778 public BluetoothDevice getDevice() {
779 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800780 }
781
782 /**
783 * Discovers services offered by a remote device as well as their
784 * characteristics and descriptors.
785 *
786 * <p>This is an asynchronous operation. Once service discovery is completed,
787 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
788 * triggered. If the discovery was successful, the remote services can be
789 * retrieved using the {@link #getServices} function.
790 *
791 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
792 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800793 * @return true, if the remote service discovery has been started
794 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800795 public boolean discoverServices() {
796 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800797 if (mService == null || mClientIf == 0) return false;
798
799 mServices.clear();
800
801 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800802 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800803 } catch (RemoteException e) {
804 Log.e(TAG,"",e);
805 return false;
806 }
807
808 return true;
809 }
810
811 /**
812 * Returns a list of GATT services offered by the remote device.
813 *
814 * <p>This function requires that service discovery has been completed
815 * for the given device.
816 *
817 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
818 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800819 * @return List of services on the remote device. Returns an empty list
820 * if service discovery has not yet been performed.
821 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800822 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800823 List<BluetoothGattService> result =
824 new ArrayList<BluetoothGattService>();
825
826 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800827 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800828 result.add(service);
829 }
830 }
831
832 return result;
833 }
834
835 /**
836 * Returns a {@link BluetoothGattService}, if the requested UUID is
837 * supported by the remote device.
838 *
839 * <p>This function requires that service discovery has been completed
840 * for the given device.
841 *
842 * <p>If multiple instances of the same service (as identified by UUID)
843 * exist, the first instance of the service is returned.
844 *
845 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
846 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800847 * @param uuid UUID of the requested service
848 * @return BluetoothGattService if supported, or null if the requested
849 * service is not offered by the remote device.
850 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800851 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800852 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 service.getUuid().equals(uuid)) {
855 return service;
856 }
857 }
858
859 return null;
860 }
861
862 /**
863 * Reads the requested characteristic from the associated remote device.
864 *
865 * <p>This is an asynchronous operation. The result of the read operation
866 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
867 * callback.
868 *
869 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
870 *
871 * @param characteristic Characteristic to read from the remote device
872 * @return true, if the read operation was initiated successfully
873 */
874 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
875 if ((characteristic.getProperties() &
876 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
877
878 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
879 if (mService == null || mClientIf == 0) return false;
880
881 BluetoothGattService service = characteristic.getService();
882 if (service == null) return false;
883
884 BluetoothDevice device = service.getDevice();
885 if (device == null) return false;
886
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700887 synchronized(mDeviceBusy) {
888 if (mDeviceBusy) return false;
889 mDeviceBusy = true;
890 }
891
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800892 try {
893 mService.readCharacteristic(mClientIf, device.getAddress(),
894 service.getType(), service.getInstanceId(),
895 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
896 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
897 } catch (RemoteException e) {
898 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700899 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800900 return false;
901 }
902
903 return true;
904 }
905
906 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800907 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800908 *
909 * <p>Once the write operation has been completed, the
910 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
911 * reporting the result of the operation.
912 *
913 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
914 *
915 * @param characteristic Characteristic to write on the remote device
916 * @return true, if the write operation was initiated successfully
917 */
918 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
919 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
920 && (characteristic.getProperties() &
921 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
922
923 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
924 if (mService == null || mClientIf == 0) return false;
925
926 BluetoothGattService service = characteristic.getService();
927 if (service == null) return false;
928
929 BluetoothDevice device = service.getDevice();
930 if (device == null) return false;
931
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700932 synchronized(mDeviceBusy) {
933 if (mDeviceBusy) return false;
934 mDeviceBusy = true;
935 }
936
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800937 try {
938 mService.writeCharacteristic(mClientIf, device.getAddress(),
939 service.getType(), service.getInstanceId(),
940 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
941 new ParcelUuid(characteristic.getUuid()),
942 characteristic.getWriteType(), AUTHENTICATION_NONE,
943 characteristic.getValue());
944 } catch (RemoteException e) {
945 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700946 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800947 return false;
948 }
949
950 return true;
951 }
952
953 /**
954 * Reads the value for a given descriptor from the associated remote device.
955 *
956 * <p>Once the read operation has been completed, the
957 * {@link BluetoothGattCallback#onDescriptorRead} callback is
958 * triggered, signaling the result of the operation.
959 *
960 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
961 *
962 * @param descriptor Descriptor value to read from the remote device
963 * @return true, if the read operation was initiated successfully
964 */
965 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
966 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
967 if (mService == null || mClientIf == 0) return false;
968
969 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
970 if (characteristic == null) return false;
971
972 BluetoothGattService service = characteristic.getService();
973 if (service == null) return false;
974
975 BluetoothDevice device = service.getDevice();
976 if (device == null) return false;
977
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700978 synchronized(mDeviceBusy) {
979 if (mDeviceBusy) return false;
980 mDeviceBusy = true;
981 }
982
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800983 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700984 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
985 service.getInstanceId(), new ParcelUuid(service.getUuid()),
986 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
987 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
988 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800989 } catch (RemoteException e) {
990 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700991 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800992 return false;
993 }
994
995 return true;
996 }
997
998 /**
999 * Write the value of a given descriptor to the associated remote device.
1000 *
1001 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1002 * triggered to report the result of the write operation.
1003 *
1004 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1005 *
1006 * @param descriptor Descriptor to write to the associated remote device
1007 * @return true, if the write operation was initiated successfully
1008 */
1009 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1010 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1011 if (mService == null || mClientIf == 0) return false;
1012
1013 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1014 if (characteristic == null) return false;
1015
1016 BluetoothGattService service = characteristic.getService();
1017 if (service == null) return false;
1018
1019 BluetoothDevice device = service.getDevice();
1020 if (device == null) return false;
1021
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001022 synchronized(mDeviceBusy) {
1023 if (mDeviceBusy) return false;
1024 mDeviceBusy = true;
1025 }
1026
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001027 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001028 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1029 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1030 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1031 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001032 characteristic.getWriteType(), AUTHENTICATION_NONE,
1033 descriptor.getValue());
1034 } catch (RemoteException e) {
1035 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001036 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001037 return false;
1038 }
1039
1040 return true;
1041 }
1042
1043 /**
1044 * Initiates a reliable write transaction for a given remote device.
1045 *
1046 * <p>Once a reliable write transaction has been initiated, all calls
1047 * to {@link #writeCharacteristic} are sent to the remote device for
1048 * verification and queued up for atomic execution. The application will
1049 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1050 * in response to every {@link #writeCharacteristic} call and is responsible
1051 * for verifying if the value has been transmitted accurately.
1052 *
1053 * <p>After all characteristics have been queued up and verified,
1054 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1055 * was not written correctly, calling {@link #abortReliableWrite} will
1056 * cancel the current transaction without commiting any values on the
1057 * remote device.
1058 *
1059 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1060 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001061 * @return true, if the reliable write transaction has been initiated
1062 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001063 public boolean beginReliableWrite() {
1064 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001065 if (mService == null || mClientIf == 0) return false;
1066
1067 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001068 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001069 } catch (RemoteException e) {
1070 Log.e(TAG,"",e);
1071 return false;
1072 }
1073
1074 return true;
1075 }
1076
1077 /**
1078 * Executes a reliable write transaction for a given remote device.
1079 *
1080 * <p>This function will commit all queued up characteristic write
1081 * operations for a given remote device.
1082 *
1083 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1084 * invoked to indicate whether the transaction has been executed correctly.
1085 *
1086 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1087 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001088 * @return true, if the request to execute the transaction has been sent
1089 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001090 public boolean executeReliableWrite() {
1091 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001092 if (mService == null || mClientIf == 0) return false;
1093
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001094 synchronized(mDeviceBusy) {
1095 if (mDeviceBusy) return false;
1096 mDeviceBusy = true;
1097 }
1098
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001099 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001100 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001101 } catch (RemoteException e) {
1102 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001103 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001104 return false;
1105 }
1106
1107 return true;
1108 }
1109
1110 /**
1111 * Cancels a reliable write transaction for a given device.
1112 *
1113 * <p>Calling this function will discard all queued characteristic write
1114 * operations for a given remote device.
1115 *
1116 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001117 */
John Du48f8b5d2013-08-19 12:20:37 -07001118 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001119 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001120 if (mService == null || mClientIf == 0) return;
1121
1122 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001123 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 } catch (RemoteException e) {
1125 Log.e(TAG,"",e);
1126 }
1127 }
1128
1129 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001130 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001131 */
1132 public void abortReliableWrite(BluetoothDevice mDevice) {
1133 abortReliableWrite();
1134 }
1135
1136 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001137 * Enable or disable notifications/indications for a given characteristic.
1138 *
1139 * <p>Once notifications are enabled for a characteristic, a
1140 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1141 * triggered if the remote device indicates that the given characteristic
1142 * has changed.
1143 *
1144 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1145 *
1146 * @param characteristic The characteristic for which to enable notifications
1147 * @param enable Set to true to enable notifications/indications
1148 * @return true, if the requested notification status was set successfully
1149 */
1150 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1151 boolean enable) {
1152 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1153 + " enable: " + enable);
1154 if (mService == null || mClientIf == 0) return false;
1155
1156 BluetoothGattService service = characteristic.getService();
1157 if (service == null) return false;
1158
1159 BluetoothDevice device = service.getDevice();
1160 if (device == null) return false;
1161
1162 try {
1163 mService.registerForNotification(mClientIf, device.getAddress(),
1164 service.getType(), service.getInstanceId(),
1165 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1166 new ParcelUuid(characteristic.getUuid()),
1167 enable);
1168 } catch (RemoteException e) {
1169 Log.e(TAG,"",e);
1170 return false;
1171 }
1172
1173 return true;
1174 }
1175
1176 /**
1177 * Clears the internal cache and forces a refresh of the services from the
1178 * remote device.
1179 * @hide
1180 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001181 public boolean refresh() {
1182 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001183 if (mService == null || mClientIf == 0) return false;
1184
1185 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001186 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001187 } catch (RemoteException e) {
1188 Log.e(TAG,"",e);
1189 return false;
1190 }
1191
1192 return true;
1193 }
1194
1195 /**
1196 * Read the RSSI for a connected remote device.
1197 *
1198 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1199 * invoked when the RSSI value has been read.
1200 *
1201 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1202 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001203 * @return true, if the RSSI value has been requested successfully
1204 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001205 public boolean readRemoteRssi() {
1206 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001207 if (mService == null || mClientIf == 0) return false;
1208
1209 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001210 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001211 } catch (RemoteException e) {
1212 Log.e(TAG,"",e);
1213 return false;
1214 }
1215
1216 return true;
1217 }
1218
1219 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001220 * Configure the MTU used for a given connection.
1221 *
1222 * <p>When performing a write request operation (write without response),
1223 * the data sent is truncated to the MTU size. This function may be used
1224 * to request a larget MTU size to be able to send more data at once.
1225 *
1226 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1227 * whether this operation was successful.
1228 *
1229 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1230 *
1231 * @return true, if the new MTU value has been requested successfully
1232 * @hide
1233 */
1234 public boolean configureMTU(int mtu) {
1235 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1236 + " mtu: " + mtu);
1237 if (mService == null || mClientIf == 0) return false;
1238
1239 try {
1240 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1241 } catch (RemoteException e) {
1242 Log.e(TAG,"",e);
1243 return false;
1244 }
1245
1246 return true;
1247 }
1248
1249 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001250 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1251 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001252 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001253 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001254 */
1255 @Override
1256 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001257 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001258 }
1259
1260 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001261 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1262 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001263 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001264 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001265 */
1266 @Override
1267 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001268 throw new UnsupportedOperationException
1269 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001270 }
1271
1272 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001273 * Not supported - please use
1274 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1275 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001276 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001277 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001278 */
1279 @Override
1280 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001281 throw new UnsupportedOperationException
1282 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001283 }
1284}