blob: e7ab8de25bcf6cd9189fe202934397e1efddd916 [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.bluetooth.BluetoothDevice;
20import android.bluetooth.BluetoothProfile;
21import android.bluetooth.BluetoothProfile.ServiceListener;
22import android.bluetooth.IBluetoothManager;
23import android.bluetooth.IBluetoothStateChangeCallback;
24
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.ServiceConnection;
29import android.os.IBinder;
30import android.os.ParcelUuid;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.util.Log;
34
35import java.util.ArrayList;
36import java.util.List;
37import java.util.UUID;
38
39/**
Matthew Xieddf7e472013-03-01 18:41:02 -080040 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080041 *
Matthew Xieddf7e472013-03-01 18:41:02 -080042 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080043 * with Bluetooth Smart or Smart Ready devices.
44 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070046 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080047 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
48 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080049 */
50public final class BluetoothGatt implements BluetoothProfile {
51 private static final String TAG = "BluetoothGatt";
52 private static final boolean DBG = true;
Matthew Xieddf7e472013-03-01 18:41:02 -080053 private static final boolean VDBG = true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080054
Matthew Xieddf7e472013-03-01 18:41:02 -080055 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080056 private IBluetoothGatt mService;
57 private BluetoothGattCallback mCallback;
58 private int mClientIf;
59 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080060 private BluetoothDevice mDevice;
61 private boolean mAutoConnect;
62 private int mConnState;
63 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070064 private Boolean mDeviceBusy = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080065
66 private static final int CONN_STATE_IDLE = 0;
67 private static final int CONN_STATE_CONNECTING = 1;
68 private static final int CONN_STATE_CONNECTED = 2;
69 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070070 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071
72 private List<BluetoothGattService> mServices;
73
Matthew Xieddf7e472013-03-01 18:41:02 -080074 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080075 public static final int GATT_SUCCESS = 0;
76
Matthew Xieddf7e472013-03-01 18:41:02 -080077 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080078 public static final int GATT_READ_NOT_PERMITTED = 0x2;
79
Matthew Xieddf7e472013-03-01 18:41:02 -080080 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080081 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
82
83 /** Insufficient authentication for a given operation */
84 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
85
86 /** The given request is not supported */
87 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
88
89 /** Insufficient encryption for a given operation */
90 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
91
92 /** A read or write operation was requested with an invalid offset */
93 public static final int GATT_INVALID_OFFSET = 0x7;
94
95 /** A write operation exceeds the maximum length of the attribute */
96 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
97
Matthew Xie90ca8072013-05-28 21:06:50 +000098 /** A GATT operation failed, errors other than the above */
99 public static final int GATT_FAILURE = 0x101;
100
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800101 /**
102 * No authentication required.
103 * @hide
104 */
105 /*package*/ static final int AUTHENTICATION_NONE = 0;
106
107 /**
108 * Authentication requested; no man-in-the-middle protection required.
109 * @hide
110 */
111 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
112
113 /**
114 * Authentication with man-in-the-middle protection requested.
115 * @hide
116 */
117 /*package*/ static final int AUTHENTICATION_MITM = 2;
118
119 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800120 * Bluetooth GATT interface callbacks
121 */
122 private final IBluetoothGattCallback mBluetoothGattCallback =
123 new IBluetoothGattCallback.Stub() {
124 /**
125 * Application interface registered - app is ready to go
126 * @hide
127 */
128 public void onClientRegistered(int status, int clientIf) {
129 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
130 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800131 if (VDBG) {
132 synchronized(mStateLock) {
133 if (mConnState != CONN_STATE_CONNECTING) {
134 Log.e(TAG, "Bad connection state: " + mConnState);
135 }
136 }
137 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800138 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800139 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700140 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800141 BluetoothProfile.STATE_DISCONNECTED);
142 synchronized(mStateLock) {
143 mConnState = CONN_STATE_IDLE;
144 }
145 return;
146 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800147 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800148 mService.clientConnect(mClientIf, mDevice.getAddress(),
149 !mAutoConnect); // autoConnect is inverse of "isDirect"
150 } catch (RemoteException e) {
151 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800152 }
153 }
154
155 /**
156 * Client connection state changed
157 * @hide
158 */
159 public void onClientConnectionState(int status, int clientIf,
160 boolean connected, String address) {
161 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
162 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800163 if (!address.equals(mDevice.getAddress())) {
164 return;
165 }
166 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
167 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800168 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700169 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800170 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700171 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800172 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800173
174 synchronized(mStateLock) {
175 if (connected) {
176 mConnState = CONN_STATE_CONNECTED;
177 } else {
178 mConnState = CONN_STATE_IDLE;
179 }
180 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700181
182 synchronized(mDeviceBusy) {
183 mDeviceBusy = false;
184 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800185 }
186
187 /**
188 * Callback reporting an LE scan result.
189 * @hide
190 */
191 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800192 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800193 }
194
195 /**
196 * A new GATT service has been discovered.
197 * The service is added to the internal list and the search
198 * continues.
199 * @hide
200 */
201 public void onGetService(String address, int srvcType,
202 int srvcInstId, ParcelUuid srvcUuid) {
203 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800204 if (!address.equals(mDevice.getAddress())) {
205 return;
206 }
207 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800208 srvcInstId, srvcType));
209 }
210
211 /**
212 * An included service has been found durig GATT discovery.
213 * The included service is added to the respective parent.
214 * @hide
215 */
216 public void onGetIncludedService(String address, int srvcType,
217 int srvcInstId, ParcelUuid srvcUuid,
218 int inclSrvcType, int inclSrvcInstId,
219 ParcelUuid inclSrvcUuid) {
220 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
221 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
222
Matthew Xieddf7e472013-03-01 18:41:02 -0800223 if (!address.equals(mDevice.getAddress())) {
224 return;
225 }
226 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800227 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800228 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800229 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
230
231 if (service != null && includedService != null) {
232 service.addIncludedService(includedService);
233 }
234 }
235
236 /**
237 * A new GATT characteristic has been discovered.
238 * Add the new characteristic to the relevant service and continue
239 * the remote device inspection.
240 * @hide
241 */
242 public void onGetCharacteristic(String address, int srvcType,
243 int srvcInstId, ParcelUuid srvcUuid,
244 int charInstId, ParcelUuid charUuid,
245 int charProps) {
246 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
247 charUuid);
248
Matthew Xieddf7e472013-03-01 18:41:02 -0800249 if (!address.equals(mDevice.getAddress())) {
250 return;
251 }
252 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800253 srvcInstId, srvcType);
254 if (service != null) {
255 service.addCharacteristic(new BluetoothGattCharacteristic(
256 service, charUuid.getUuid(), charInstId, charProps, 0));
257 }
258 }
259
260 /**
261 * A new GATT descriptor has been discovered.
262 * Finally, add the descriptor to the related characteristic.
263 * This should conclude the remote device update.
264 * @hide
265 */
266 public void onGetDescriptor(String address, int srvcType,
267 int srvcInstId, ParcelUuid srvcUuid,
268 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700269 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800270 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
271
Matthew Xieddf7e472013-03-01 18:41:02 -0800272 if (!address.equals(mDevice.getAddress())) {
273 return;
274 }
275 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800276 srvcInstId, srvcType);
277 if (service == null) return;
278
279 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
280 charUuid.getUuid());
281 if (characteristic == null) return;
282
283 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700284 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800285 }
286
287 /**
288 * Remote search has been completed.
289 * The internal object structure should now reflect the state
290 * of the remote device database. Let the application know that
291 * we are done at this point.
292 * @hide
293 */
294 public void onSearchComplete(String address, int status) {
295 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800296 if (!address.equals(mDevice.getAddress())) {
297 return;
298 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800299 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700300 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800301 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700302 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800303 }
304 }
305
306 /**
307 * Remote characteristic has been read.
308 * Updates the internal value.
309 * @hide
310 */
311 public void onCharacteristicRead(String address, int status, int srvcType,
312 int srvcInstId, ParcelUuid srvcUuid,
313 int charInstId, ParcelUuid charUuid, byte[] value) {
314 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
315 + " UUID=" + charUuid + " Status=" + status);
316
Matthew Xieddf7e472013-03-01 18:41:02 -0800317 if (!address.equals(mDevice.getAddress())) {
318 return;
319 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700320
321 synchronized(mDeviceBusy) {
322 mDeviceBusy = false;
323 }
324
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800325 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
326 || status == GATT_INSUFFICIENT_ENCRYPTION)
327 && mAuthRetry == false) {
328 try {
329 mAuthRetry = true;
330 mService.readCharacteristic(mClientIf, address,
331 srvcType, srvcInstId, srvcUuid,
332 charInstId, charUuid, AUTHENTICATION_MITM);
333 return;
334 } catch (RemoteException e) {
335 Log.e(TAG,"",e);
336 }
337 }
338
339 mAuthRetry = false;
340
Matthew Xieddf7e472013-03-01 18:41:02 -0800341 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800342 srvcInstId, srvcType);
343 if (service == null) return;
344
345 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
346 charUuid.getUuid(), charInstId);
347 if (characteristic == null) return;
348
349 if (status == 0) characteristic.setValue(value);
350
351 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700352 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800353 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700354 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800355 }
356 }
357
358 /**
359 * Characteristic has been written to the remote device.
360 * Let the app know how we did...
361 * @hide
362 */
363 public void onCharacteristicWrite(String address, int status, int srvcType,
364 int srvcInstId, ParcelUuid srvcUuid,
365 int charInstId, ParcelUuid charUuid) {
366 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
367 + " UUID=" + charUuid + " Status=" + status);
368
Matthew Xieddf7e472013-03-01 18:41:02 -0800369 if (!address.equals(mDevice.getAddress())) {
370 return;
371 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700372
373 synchronized(mDeviceBusy) {
374 mDeviceBusy = false;
375 }
376
Matthew Xieddf7e472013-03-01 18:41:02 -0800377 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800378 srvcInstId, srvcType);
379 if (service == null) return;
380
381 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
382 charUuid.getUuid(), charInstId);
383 if (characteristic == null) return;
384
385 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
386 || status == GATT_INSUFFICIENT_ENCRYPTION)
387 && mAuthRetry == false) {
388 try {
389 mAuthRetry = true;
390 mService.writeCharacteristic(mClientIf, address,
391 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
392 characteristic.getWriteType(), AUTHENTICATION_MITM,
393 characteristic.getValue());
394 return;
395 } catch (RemoteException e) {
396 Log.e(TAG,"",e);
397 }
398 }
399
400 mAuthRetry = false;
401
402 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700403 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800404 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700405 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800406 }
407 }
408
409 /**
410 * Remote characteristic has been updated.
411 * Updates the internal value.
412 * @hide
413 */
414 public void onNotify(String address, int srvcType,
415 int srvcInstId, ParcelUuid srvcUuid,
416 int charInstId, ParcelUuid charUuid,
417 byte[] value) {
418 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
419
Matthew Xieddf7e472013-03-01 18:41:02 -0800420 if (!address.equals(mDevice.getAddress())) {
421 return;
422 }
423 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800424 srvcInstId, srvcType);
425 if (service == null) return;
426
427 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
428 charUuid.getUuid(), charInstId);
429 if (characteristic == null) return;
430
431 characteristic.setValue(value);
432
433 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700434 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800435 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700436 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800437 }
438 }
439
440 /**
441 * Descriptor has been read.
442 * @hide
443 */
444 public void onDescriptorRead(String address, int status, int srvcType,
445 int srvcInstId, ParcelUuid srvcUuid,
446 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700447 int descrInstId, ParcelUuid descrUuid,
448 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800449 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
450
Matthew Xieddf7e472013-03-01 18:41:02 -0800451 if (!address.equals(mDevice.getAddress())) {
452 return;
453 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700454
455 synchronized(mDeviceBusy) {
456 mDeviceBusy = false;
457 }
458
Matthew Xieddf7e472013-03-01 18:41:02 -0800459 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800460 srvcInstId, srvcType);
461 if (service == null) return;
462
463 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
464 charUuid.getUuid(), charInstId);
465 if (characteristic == null) return;
466
467 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700468 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800469 if (descriptor == null) return;
470
471 if (status == 0) descriptor.setValue(value);
472
473 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
474 || status == GATT_INSUFFICIENT_ENCRYPTION)
475 && mAuthRetry == false) {
476 try {
477 mAuthRetry = true;
478 mService.readDescriptor(mClientIf, address,
479 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700480 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800481 } catch (RemoteException e) {
482 Log.e(TAG,"",e);
483 }
484 }
485
486 mAuthRetry = true;
487
488 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700489 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800490 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700491 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800492 }
493 }
494
495 /**
496 * Descriptor write operation complete.
497 * @hide
498 */
499 public void onDescriptorWrite(String address, int status, int srvcType,
500 int srvcInstId, ParcelUuid srvcUuid,
501 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700502 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800503 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
504
Matthew Xieddf7e472013-03-01 18:41:02 -0800505 if (!address.equals(mDevice.getAddress())) {
506 return;
507 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700508
509 synchronized(mDeviceBusy) {
510 mDeviceBusy = false;
511 }
512
Matthew Xieddf7e472013-03-01 18:41:02 -0800513 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800514 srvcInstId, srvcType);
515 if (service == null) return;
516
517 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
518 charUuid.getUuid(), charInstId);
519 if (characteristic == null) return;
520
521 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700522 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 if (descriptor == null) return;
524
525 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
526 || status == GATT_INSUFFICIENT_ENCRYPTION)
527 && mAuthRetry == false) {
528 try {
529 mAuthRetry = true;
530 mService.writeDescriptor(mClientIf, address,
531 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700532 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800533 AUTHENTICATION_MITM, descriptor.getValue());
534 } catch (RemoteException e) {
535 Log.e(TAG,"",e);
536 }
537 }
538
539 mAuthRetry = false;
540
541 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700542 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800543 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700544 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800545 }
546 }
547
548 /**
549 * Prepared write transaction completed (or aborted)
550 * @hide
551 */
552 public void onExecuteWrite(String address, int status) {
553 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
554 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800555 if (!address.equals(mDevice.getAddress())) {
556 return;
557 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700558
559 synchronized(mDeviceBusy) {
560 mDeviceBusy = false;
561 }
562
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800563 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700564 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800565 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700566 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800567 }
568 }
569
570 /**
571 * Remote device RSSI has been read
572 * @hide
573 */
574 public void onReadRemoteRssi(String address, int rssi, int status) {
575 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
576 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800577 if (!address.equals(mDevice.getAddress())) {
578 return;
579 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700581 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700583 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800584 }
585 }
Wei Wang4b4eebb2014-03-11 22:22:41 -0700586
587 /**
588 * Advertise state change callback
589 * @hide
590 */
591 public void onAdvertiseStateChange(int state, int status) {
592 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
593 + state + " status=" + status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700594 }
595
596 /**
597 * Callback invoked when the MTU for a given connection changes
598 * @hide
599 */
600 public void onConfigureMTU(String address, int mtu, int status) {
601 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
602 " mtu=" + mtu + " status=" + status);
603 if (!address.equals(mDevice.getAddress())) {
604 return;
605 }
606 try {
607 mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status);
608 } catch (Exception ex) {
609 Log.w(TAG, "Unhandled exception in callback", ex);
610 }
Wei Wang4b4eebb2014-03-11 22:22:41 -0700611 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800612 };
613
Matthew Xieddf7e472013-03-01 18:41:02 -0800614 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800615 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800616 mService = iGatt;
617 mDevice = device;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800618 mServices = new ArrayList<BluetoothGattService>();
619
Matthew Xieddf7e472013-03-01 18:41:02 -0800620 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800621 }
622
623 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700624 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700625 *
626 * Application should call this method as early as possible after it is done with
627 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800628 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700629 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800630 if (DBG) Log.d(TAG, "close()");
631
632 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700633 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800634 }
635
636 /**
637 * Returns a service by UUID, instance and type.
638 * @hide
639 */
640 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
641 int instanceId, int type) {
642 for(BluetoothGattService svc : mServices) {
643 if (svc.getDevice().equals(device) &&
644 svc.getType() == type &&
645 svc.getInstanceId() == instanceId &&
646 svc.getUuid().equals(uuid)) {
647 return svc;
648 }
649 }
650 return null;
651 }
652
653
654 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800655 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800656 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800657 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
658 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800659 *
660 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
661 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800662 * @param callback GATT callback handler that will receive asynchronous callbacks.
663 * @return If true, the callback will be called to notify success or failure,
664 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800665 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800666 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800667 if (DBG) Log.d(TAG, "registerApp()");
668 if (mService == null) return false;
669
670 mCallback = callback;
671 UUID uuid = UUID.randomUUID();
672 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
673
674 try {
675 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
676 } catch (RemoteException e) {
677 Log.e(TAG,"",e);
678 return false;
679 }
680
681 return true;
682 }
683
684 /**
685 * Unregister the current application and callbacks.
686 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800687 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800688 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
689 if (mService == null || mClientIf == 0) return;
690
691 try {
692 mCallback = null;
693 mService.unregisterClient(mClientIf);
694 mClientIf = 0;
695 } catch (RemoteException e) {
696 Log.e(TAG,"",e);
697 }
698 }
699
700 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800701 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 *
703 * <p>The connection may not be established right away, but will be
704 * completed when the remote device is available. A
705 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
706 * invoked when the connection state changes as a result of this function.
707 *
708 * <p>The autoConnect paramter determines whether to actively connect to
709 * the remote device, or rather passively scan and finalize the connection
710 * when the remote device is in range/available. Generally, the first ever
711 * connection to a device should be direct (autoConnect set to false) and
712 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800713 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800714 *
715 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
716 *
717 * @param device Remote device to connect to
718 * @param autoConnect Whether to directly connect to the remote device (false)
719 * or to automatically connect as soon as the remote
720 * device becomes available (true).
721 * @return true, if the connection attempt was initiated successfully
722 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800723 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
724 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
725 synchronized(mStateLock) {
726 if (mConnState != CONN_STATE_IDLE) {
727 throw new IllegalStateException("Not idle");
728 }
729 mConnState = CONN_STATE_CONNECTING;
730 }
731 if (!registerApp(callback)) {
732 synchronized(mStateLock) {
733 mConnState = CONN_STATE_IDLE;
734 }
735 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800736 return false;
737 }
738
Matthew Xieddf7e472013-03-01 18:41:02 -0800739 // the connection will continue after successful callback registration
740 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800741 return true;
742 }
743
744 /**
745 * Disconnects an established connection, or cancels a connection attempt
746 * currently in progress.
747 *
748 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800749 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800750 public void disconnect() {
751 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800752 if (mService == null || mClientIf == 0) return;
753
754 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800755 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800756 } catch (RemoteException e) {
757 Log.e(TAG,"",e);
758 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700759 }
760
761 /**
762 * Connect back to remote device.
763 *
764 * <p>This method is used to re-connect to a remote device after the
765 * connection has been dropped. If the device is not in range, the
766 * re-connection will be triggered once the device is back in range.
767 *
768 * @return true, if the connection attempt was initiated successfully
769 */
770 public boolean connect() {
771 try {
772 mService.clientConnect(mClientIf, mDevice.getAddress(),
773 false); // autoConnect is inverse of "isDirect"
774 return true;
775 } catch (RemoteException e) {
776 Log.e(TAG,"",e);
777 return false;
778 }
779 }
780
781 /**
782 * Return the remote bluetooth device this GATT client targets to
783 *
784 * @return remote bluetooth device
785 */
786 public BluetoothDevice getDevice() {
787 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800788 }
789
790 /**
791 * Discovers services offered by a remote device as well as their
792 * characteristics and descriptors.
793 *
794 * <p>This is an asynchronous operation. Once service discovery is completed,
795 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
796 * triggered. If the discovery was successful, the remote services can be
797 * retrieved using the {@link #getServices} function.
798 *
799 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
800 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800801 * @return true, if the remote service discovery has been started
802 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800803 public boolean discoverServices() {
804 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800805 if (mService == null || mClientIf == 0) return false;
806
807 mServices.clear();
808
809 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800810 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800811 } catch (RemoteException e) {
812 Log.e(TAG,"",e);
813 return false;
814 }
815
816 return true;
817 }
818
819 /**
820 * Returns a list of GATT services offered by the remote device.
821 *
822 * <p>This function requires that service discovery has been completed
823 * for the given device.
824 *
825 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
826 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800827 * @return List of services on the remote device. Returns an empty list
828 * if service discovery has not yet been performed.
829 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800830 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800831 List<BluetoothGattService> result =
832 new ArrayList<BluetoothGattService>();
833
834 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800835 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800836 result.add(service);
837 }
838 }
839
840 return result;
841 }
842
843 /**
844 * Returns a {@link BluetoothGattService}, if the requested UUID is
845 * supported by the remote device.
846 *
847 * <p>This function requires that service discovery has been completed
848 * for the given device.
849 *
850 * <p>If multiple instances of the same service (as identified by UUID)
851 * exist, the first instance of the service is returned.
852 *
853 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
854 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800855 * @param uuid UUID of the requested service
856 * @return BluetoothGattService if supported, or null if the requested
857 * service is not offered by the remote device.
858 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800859 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800860 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800861 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800862 service.getUuid().equals(uuid)) {
863 return service;
864 }
865 }
866
867 return null;
868 }
869
870 /**
871 * Reads the requested characteristic from the associated remote device.
872 *
873 * <p>This is an asynchronous operation. The result of the read operation
874 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
875 * callback.
876 *
877 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
878 *
879 * @param characteristic Characteristic to read from the remote device
880 * @return true, if the read operation was initiated successfully
881 */
882 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
883 if ((characteristic.getProperties() &
884 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
885
886 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
887 if (mService == null || mClientIf == 0) return false;
888
889 BluetoothGattService service = characteristic.getService();
890 if (service == null) return false;
891
892 BluetoothDevice device = service.getDevice();
893 if (device == null) return false;
894
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700895 synchronized(mDeviceBusy) {
896 if (mDeviceBusy) return false;
897 mDeviceBusy = true;
898 }
899
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800900 try {
901 mService.readCharacteristic(mClientIf, device.getAddress(),
902 service.getType(), service.getInstanceId(),
903 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
904 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
905 } catch (RemoteException e) {
906 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700907 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800908 return false;
909 }
910
911 return true;
912 }
913
914 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800915 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800916 *
917 * <p>Once the write operation has been completed, the
918 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
919 * reporting the result of the operation.
920 *
921 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
922 *
923 * @param characteristic Characteristic to write on the remote device
924 * @return true, if the write operation was initiated successfully
925 */
926 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
927 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
928 && (characteristic.getProperties() &
929 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
930
931 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
932 if (mService == null || mClientIf == 0) return false;
933
934 BluetoothGattService service = characteristic.getService();
935 if (service == null) return false;
936
937 BluetoothDevice device = service.getDevice();
938 if (device == null) return false;
939
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700940 synchronized(mDeviceBusy) {
941 if (mDeviceBusy) return false;
942 mDeviceBusy = true;
943 }
944
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800945 try {
946 mService.writeCharacteristic(mClientIf, device.getAddress(),
947 service.getType(), service.getInstanceId(),
948 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
949 new ParcelUuid(characteristic.getUuid()),
950 characteristic.getWriteType(), AUTHENTICATION_NONE,
951 characteristic.getValue());
952 } catch (RemoteException e) {
953 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700954 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800955 return false;
956 }
957
958 return true;
959 }
960
961 /**
962 * Reads the value for a given descriptor from the associated remote device.
963 *
964 * <p>Once the read operation has been completed, the
965 * {@link BluetoothGattCallback#onDescriptorRead} callback is
966 * triggered, signaling the result of the operation.
967 *
968 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
969 *
970 * @param descriptor Descriptor value to read from the remote device
971 * @return true, if the read operation was initiated successfully
972 */
973 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
974 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
975 if (mService == null || mClientIf == 0) return false;
976
977 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
978 if (characteristic == null) return false;
979
980 BluetoothGattService service = characteristic.getService();
981 if (service == null) return false;
982
983 BluetoothDevice device = service.getDevice();
984 if (device == null) return false;
985
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700986 synchronized(mDeviceBusy) {
987 if (mDeviceBusy) return false;
988 mDeviceBusy = true;
989 }
990
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800991 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700992 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
993 service.getInstanceId(), new ParcelUuid(service.getUuid()),
994 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
995 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
996 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800997 } catch (RemoteException e) {
998 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700999 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001000 return false;
1001 }
1002
1003 return true;
1004 }
1005
1006 /**
1007 * Write the value of a given descriptor to the associated remote device.
1008 *
1009 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1010 * triggered to report the result of the write operation.
1011 *
1012 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1013 *
1014 * @param descriptor Descriptor to write to the associated remote device
1015 * @return true, if the write operation was initiated successfully
1016 */
1017 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1018 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1019 if (mService == null || mClientIf == 0) return false;
1020
1021 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1022 if (characteristic == null) return false;
1023
1024 BluetoothGattService service = characteristic.getService();
1025 if (service == null) return false;
1026
1027 BluetoothDevice device = service.getDevice();
1028 if (device == null) return false;
1029
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001030 synchronized(mDeviceBusy) {
1031 if (mDeviceBusy) return false;
1032 mDeviceBusy = true;
1033 }
1034
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001035 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001036 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1037 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1038 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1039 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001040 characteristic.getWriteType(), AUTHENTICATION_NONE,
1041 descriptor.getValue());
1042 } catch (RemoteException e) {
1043 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001044 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001045 return false;
1046 }
1047
1048 return true;
1049 }
1050
1051 /**
1052 * Initiates a reliable write transaction for a given remote device.
1053 *
1054 * <p>Once a reliable write transaction has been initiated, all calls
1055 * to {@link #writeCharacteristic} are sent to the remote device for
1056 * verification and queued up for atomic execution. The application will
1057 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1058 * in response to every {@link #writeCharacteristic} call and is responsible
1059 * for verifying if the value has been transmitted accurately.
1060 *
1061 * <p>After all characteristics have been queued up and verified,
1062 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1063 * was not written correctly, calling {@link #abortReliableWrite} will
1064 * cancel the current transaction without commiting any values on the
1065 * remote device.
1066 *
1067 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1068 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001069 * @return true, if the reliable write transaction has been initiated
1070 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001071 public boolean beginReliableWrite() {
1072 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001073 if (mService == null || mClientIf == 0) return false;
1074
1075 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001076 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001077 } catch (RemoteException e) {
1078 Log.e(TAG,"",e);
1079 return false;
1080 }
1081
1082 return true;
1083 }
1084
1085 /**
1086 * Executes a reliable write transaction for a given remote device.
1087 *
1088 * <p>This function will commit all queued up characteristic write
1089 * operations for a given remote device.
1090 *
1091 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1092 * invoked to indicate whether the transaction has been executed correctly.
1093 *
1094 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1095 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001096 * @return true, if the request to execute the transaction has been sent
1097 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001098 public boolean executeReliableWrite() {
1099 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001100 if (mService == null || mClientIf == 0) return false;
1101
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001102 synchronized(mDeviceBusy) {
1103 if (mDeviceBusy) return false;
1104 mDeviceBusy = true;
1105 }
1106
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001107 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001108 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001109 } catch (RemoteException e) {
1110 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001111 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001112 return false;
1113 }
1114
1115 return true;
1116 }
1117
1118 /**
1119 * Cancels a reliable write transaction for a given device.
1120 *
1121 * <p>Calling this function will discard all queued characteristic write
1122 * operations for a given remote device.
1123 *
1124 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001125 */
John Du48f8b5d2013-08-19 12:20:37 -07001126 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001127 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001128 if (mService == null || mClientIf == 0) return;
1129
1130 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001131 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001132 } catch (RemoteException e) {
1133 Log.e(TAG,"",e);
1134 }
1135 }
1136
1137 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001138 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001139 */
1140 public void abortReliableWrite(BluetoothDevice mDevice) {
1141 abortReliableWrite();
1142 }
1143
1144 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001145 * Enable or disable notifications/indications for a given characteristic.
1146 *
1147 * <p>Once notifications are enabled for a characteristic, a
1148 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1149 * triggered if the remote device indicates that the given characteristic
1150 * has changed.
1151 *
1152 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1153 *
1154 * @param characteristic The characteristic for which to enable notifications
1155 * @param enable Set to true to enable notifications/indications
1156 * @return true, if the requested notification status was set successfully
1157 */
1158 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1159 boolean enable) {
1160 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1161 + " enable: " + enable);
1162 if (mService == null || mClientIf == 0) return false;
1163
1164 BluetoothGattService service = characteristic.getService();
1165 if (service == null) return false;
1166
1167 BluetoothDevice device = service.getDevice();
1168 if (device == null) return false;
1169
1170 try {
1171 mService.registerForNotification(mClientIf, device.getAddress(),
1172 service.getType(), service.getInstanceId(),
1173 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1174 new ParcelUuid(characteristic.getUuid()),
1175 enable);
1176 } catch (RemoteException e) {
1177 Log.e(TAG,"",e);
1178 return false;
1179 }
1180
1181 return true;
1182 }
1183
1184 /**
1185 * Clears the internal cache and forces a refresh of the services from the
1186 * remote device.
1187 * @hide
1188 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001189 public boolean refresh() {
1190 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001191 if (mService == null || mClientIf == 0) return false;
1192
1193 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001194 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001195 } catch (RemoteException e) {
1196 Log.e(TAG,"",e);
1197 return false;
1198 }
1199
1200 return true;
1201 }
1202
1203 /**
1204 * Read the RSSI for a connected remote device.
1205 *
1206 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1207 * invoked when the RSSI value has been read.
1208 *
1209 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1210 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001211 * @return true, if the RSSI value has been requested successfully
1212 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001213 public boolean readRemoteRssi() {
1214 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001215 if (mService == null || mClientIf == 0) return false;
1216
1217 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001218 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001219 } catch (RemoteException e) {
1220 Log.e(TAG,"",e);
1221 return false;
1222 }
1223
1224 return true;
1225 }
1226
1227 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001228 * Configure the MTU used for a given connection.
1229 *
1230 * <p>When performing a write request operation (write without response),
1231 * the data sent is truncated to the MTU size. This function may be used
1232 * to request a larget MTU size to be able to send more data at once.
1233 *
1234 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1235 * whether this operation was successful.
1236 *
1237 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1238 *
1239 * @return true, if the new MTU value has been requested successfully
1240 * @hide
1241 */
1242 public boolean configureMTU(int mtu) {
1243 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1244 + " mtu: " + mtu);
1245 if (mService == null || mClientIf == 0) return false;
1246
1247 try {
1248 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1249 } catch (RemoteException e) {
1250 Log.e(TAG,"",e);
1251 return false;
1252 }
1253
1254 return true;
1255 }
1256
1257 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001258 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1259 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001260 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001261 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001262 */
1263 @Override
1264 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001265 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001266 }
1267
1268 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001269 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1270 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001271 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001272 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001273 */
1274 @Override
1275 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001276 throw new UnsupportedOperationException
1277 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001278 }
1279
1280 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001281 * Not supported - please use
1282 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1283 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001284 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001285 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001286 */
1287 @Override
1288 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001289 throw new UnsupportedOperationException
1290 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001291 }
1292}