blob: b390aa12b95cbc973c961d4edabb1f926f3cb4c6 [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();
64
65 private static final int CONN_STATE_IDLE = 0;
66 private static final int CONN_STATE_CONNECTING = 1;
67 private static final int CONN_STATE_CONNECTED = 2;
68 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070069 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080070
71 private List<BluetoothGattService> mServices;
72
Matthew Xieddf7e472013-03-01 18:41:02 -080073 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080074 public static final int GATT_SUCCESS = 0;
75
Matthew Xieddf7e472013-03-01 18:41:02 -080076 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080077 public static final int GATT_READ_NOT_PERMITTED = 0x2;
78
Matthew Xieddf7e472013-03-01 18:41:02 -080079 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080080 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
81
82 /** Insufficient authentication for a given operation */
83 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
84
85 /** The given request is not supported */
86 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
87
88 /** Insufficient encryption for a given operation */
89 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
90
91 /** A read or write operation was requested with an invalid offset */
92 public static final int GATT_INVALID_OFFSET = 0x7;
93
94 /** A write operation exceeds the maximum length of the attribute */
95 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
96
Matthew Xie90ca8072013-05-28 21:06:50 +000097 /** A GATT operation failed, errors other than the above */
98 public static final int GATT_FAILURE = 0x101;
99
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800100 /**
101 * No authentication required.
102 * @hide
103 */
104 /*package*/ static final int AUTHENTICATION_NONE = 0;
105
106 /**
107 * Authentication requested; no man-in-the-middle protection required.
108 * @hide
109 */
110 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
111
112 /**
113 * Authentication with man-in-the-middle protection requested.
114 * @hide
115 */
116 /*package*/ static final int AUTHENTICATION_MITM = 2;
117
118 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800119 * Bluetooth GATT interface callbacks
120 */
121 private final IBluetoothGattCallback mBluetoothGattCallback =
122 new IBluetoothGattCallback.Stub() {
123 /**
124 * Application interface registered - app is ready to go
125 * @hide
126 */
127 public void onClientRegistered(int status, int clientIf) {
128 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
129 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800130 if (VDBG) {
131 synchronized(mStateLock) {
132 if (mConnState != CONN_STATE_CONNECTING) {
133 Log.e(TAG, "Bad connection state: " + mConnState);
134 }
135 }
136 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800138 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700139 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800140 BluetoothProfile.STATE_DISCONNECTED);
141 synchronized(mStateLock) {
142 mConnState = CONN_STATE_IDLE;
143 }
144 return;
145 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800146 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800147 mService.clientConnect(mClientIf, mDevice.getAddress(),
148 !mAutoConnect); // autoConnect is inverse of "isDirect"
149 } catch (RemoteException e) {
150 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800151 }
152 }
153
154 /**
155 * Client connection state changed
156 * @hide
157 */
158 public void onClientConnectionState(int status, int clientIf,
159 boolean connected, String address) {
160 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
161 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800162 if (!address.equals(mDevice.getAddress())) {
163 return;
164 }
165 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
166 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800167 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700168 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800169 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700170 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800171 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800172
173 synchronized(mStateLock) {
174 if (connected) {
175 mConnState = CONN_STATE_CONNECTED;
176 } else {
177 mConnState = CONN_STATE_IDLE;
178 }
179 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800180 }
181
182 /**
183 * Callback reporting an LE scan result.
184 * @hide
185 */
186 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800187 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800188 }
189
190 /**
191 * A new GATT service has been discovered.
192 * The service is added to the internal list and the search
193 * continues.
194 * @hide
195 */
196 public void onGetService(String address, int srvcType,
197 int srvcInstId, ParcelUuid srvcUuid) {
198 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800199 if (!address.equals(mDevice.getAddress())) {
200 return;
201 }
202 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800203 srvcInstId, srvcType));
204 }
205
206 /**
207 * An included service has been found durig GATT discovery.
208 * The included service is added to the respective parent.
209 * @hide
210 */
211 public void onGetIncludedService(String address, int srvcType,
212 int srvcInstId, ParcelUuid srvcUuid,
213 int inclSrvcType, int inclSrvcInstId,
214 ParcelUuid inclSrvcUuid) {
215 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
216 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
217
Matthew Xieddf7e472013-03-01 18:41:02 -0800218 if (!address.equals(mDevice.getAddress())) {
219 return;
220 }
221 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800222 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800223 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800224 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
225
226 if (service != null && includedService != null) {
227 service.addIncludedService(includedService);
228 }
229 }
230
231 /**
232 * A new GATT characteristic has been discovered.
233 * Add the new characteristic to the relevant service and continue
234 * the remote device inspection.
235 * @hide
236 */
237 public void onGetCharacteristic(String address, int srvcType,
238 int srvcInstId, ParcelUuid srvcUuid,
239 int charInstId, ParcelUuid charUuid,
240 int charProps) {
241 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
242 charUuid);
243
Matthew Xieddf7e472013-03-01 18:41:02 -0800244 if (!address.equals(mDevice.getAddress())) {
245 return;
246 }
247 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800248 srvcInstId, srvcType);
249 if (service != null) {
250 service.addCharacteristic(new BluetoothGattCharacteristic(
251 service, charUuid.getUuid(), charInstId, charProps, 0));
252 }
253 }
254
255 /**
256 * A new GATT descriptor has been discovered.
257 * Finally, add the descriptor to the related characteristic.
258 * This should conclude the remote device update.
259 * @hide
260 */
261 public void onGetDescriptor(String address, int srvcType,
262 int srvcInstId, ParcelUuid srvcUuid,
263 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700264 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800265 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
266
Matthew Xieddf7e472013-03-01 18:41:02 -0800267 if (!address.equals(mDevice.getAddress())) {
268 return;
269 }
270 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800271 srvcInstId, srvcType);
272 if (service == null) return;
273
274 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
275 charUuid.getUuid());
276 if (characteristic == null) return;
277
278 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700279 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800280 }
281
282 /**
283 * Remote search has been completed.
284 * The internal object structure should now reflect the state
285 * of the remote device database. Let the application know that
286 * we are done at this point.
287 * @hide
288 */
289 public void onSearchComplete(String address, int status) {
290 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800291 if (!address.equals(mDevice.getAddress())) {
292 return;
293 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800294 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700295 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800296 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700297 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800298 }
299 }
300
301 /**
302 * Remote characteristic has been read.
303 * Updates the internal value.
304 * @hide
305 */
306 public void onCharacteristicRead(String address, int status, int srvcType,
307 int srvcInstId, ParcelUuid srvcUuid,
308 int charInstId, ParcelUuid charUuid, byte[] value) {
309 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
310 + " UUID=" + charUuid + " Status=" + status);
311
Matthew Xieddf7e472013-03-01 18:41:02 -0800312 if (!address.equals(mDevice.getAddress())) {
313 return;
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 }
362 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800363 srvcInstId, srvcType);
364 if (service == null) return;
365
366 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
367 charUuid.getUuid(), charInstId);
368 if (characteristic == null) return;
369
370 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
371 || status == GATT_INSUFFICIENT_ENCRYPTION)
372 && mAuthRetry == false) {
373 try {
374 mAuthRetry = true;
375 mService.writeCharacteristic(mClientIf, address,
376 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
377 characteristic.getWriteType(), AUTHENTICATION_MITM,
378 characteristic.getValue());
379 return;
380 } catch (RemoteException e) {
381 Log.e(TAG,"",e);
382 }
383 }
384
385 mAuthRetry = false;
386
387 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700388 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800389 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700390 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800391 }
392 }
393
394 /**
395 * Remote characteristic has been updated.
396 * Updates the internal value.
397 * @hide
398 */
399 public void onNotify(String address, int srvcType,
400 int srvcInstId, ParcelUuid srvcUuid,
401 int charInstId, ParcelUuid charUuid,
402 byte[] value) {
403 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
404
Matthew Xieddf7e472013-03-01 18:41:02 -0800405 if (!address.equals(mDevice.getAddress())) {
406 return;
407 }
408 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800409 srvcInstId, srvcType);
410 if (service == null) return;
411
412 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
413 charUuid.getUuid(), charInstId);
414 if (characteristic == null) return;
415
416 characteristic.setValue(value);
417
418 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700419 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800420 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700421 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800422 }
423 }
424
425 /**
426 * Descriptor has been read.
427 * @hide
428 */
429 public void onDescriptorRead(String address, int status, int srvcType,
430 int srvcInstId, ParcelUuid srvcUuid,
431 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700432 int descrInstId, ParcelUuid descrUuid,
433 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800434 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
435
Matthew Xieddf7e472013-03-01 18:41:02 -0800436 if (!address.equals(mDevice.getAddress())) {
437 return;
438 }
439 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440 srvcInstId, srvcType);
441 if (service == null) return;
442
443 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
444 charUuid.getUuid(), charInstId);
445 if (characteristic == null) return;
446
447 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700448 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800449 if (descriptor == null) return;
450
451 if (status == 0) descriptor.setValue(value);
452
453 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
454 || status == GATT_INSUFFICIENT_ENCRYPTION)
455 && mAuthRetry == false) {
456 try {
457 mAuthRetry = true;
458 mService.readDescriptor(mClientIf, address,
459 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700460 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800461 } catch (RemoteException e) {
462 Log.e(TAG,"",e);
463 }
464 }
465
466 mAuthRetry = true;
467
468 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700469 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800470 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700471 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800472 }
473 }
474
475 /**
476 * Descriptor write operation complete.
477 * @hide
478 */
479 public void onDescriptorWrite(String address, int status, int srvcType,
480 int srvcInstId, ParcelUuid srvcUuid,
481 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700482 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800483 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
484
Matthew Xieddf7e472013-03-01 18:41:02 -0800485 if (!address.equals(mDevice.getAddress())) {
486 return;
487 }
488 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800489 srvcInstId, srvcType);
490 if (service == null) return;
491
492 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
493 charUuid.getUuid(), charInstId);
494 if (characteristic == null) return;
495
496 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700497 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800498 if (descriptor == null) return;
499
500 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
501 || status == GATT_INSUFFICIENT_ENCRYPTION)
502 && mAuthRetry == false) {
503 try {
504 mAuthRetry = true;
505 mService.writeDescriptor(mClientIf, address,
506 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700507 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800508 AUTHENTICATION_MITM, descriptor.getValue());
509 } catch (RemoteException e) {
510 Log.e(TAG,"",e);
511 }
512 }
513
514 mAuthRetry = false;
515
516 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700517 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800518 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700519 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800520 }
521 }
522
523 /**
524 * Prepared write transaction completed (or aborted)
525 * @hide
526 */
527 public void onExecuteWrite(String address, int status) {
528 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
529 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800530 if (!address.equals(mDevice.getAddress())) {
531 return;
532 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800533 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700534 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800535 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700536 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800537 }
538 }
539
540 /**
541 * Remote device RSSI has been read
542 * @hide
543 */
544 public void onReadRemoteRssi(String address, int rssi, int status) {
545 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
546 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800547 if (!address.equals(mDevice.getAddress())) {
548 return;
549 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800550 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700551 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800552 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700553 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800554 }
555 }
Andre Eisenbachf46b21a2013-08-06 19:57:48 -0700556
557 /**
558 * Listen command status callback
559 * @hide
560 */
561 public void onListen(int status) {
562 if (DBG) Log.d(TAG, "onListen() - status=" + status);
563 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800564 };
565
Matthew Xieddf7e472013-03-01 18:41:02 -0800566 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800567 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800568 mService = iGatt;
569 mDevice = device;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800570 mServices = new ArrayList<BluetoothGattService>();
571
Matthew Xieddf7e472013-03-01 18:41:02 -0800572 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800573 }
574
575 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700576 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700577 *
578 * Application should call this method as early as possible after it is done with
579 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700581 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 if (DBG) Log.d(TAG, "close()");
583
584 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700585 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800586 }
587
588 /**
589 * Returns a service by UUID, instance and type.
590 * @hide
591 */
592 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
593 int instanceId, int type) {
594 for(BluetoothGattService svc : mServices) {
595 if (svc.getDevice().equals(device) &&
596 svc.getType() == type &&
597 svc.getInstanceId() == instanceId &&
598 svc.getUuid().equals(uuid)) {
599 return svc;
600 }
601 }
602 return null;
603 }
604
605
606 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800607 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800608 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800609 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
610 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800611 *
612 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
613 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800614 * @param callback GATT callback handler that will receive asynchronous callbacks.
615 * @return If true, the callback will be called to notify success or failure,
616 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800617 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800618 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800619 if (DBG) Log.d(TAG, "registerApp()");
620 if (mService == null) return false;
621
622 mCallback = callback;
623 UUID uuid = UUID.randomUUID();
624 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
625
626 try {
627 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
628 } catch (RemoteException e) {
629 Log.e(TAG,"",e);
630 return false;
631 }
632
633 return true;
634 }
635
636 /**
637 * Unregister the current application and callbacks.
638 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800639 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800640 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
641 if (mService == null || mClientIf == 0) return;
642
643 try {
644 mCallback = null;
645 mService.unregisterClient(mClientIf);
646 mClientIf = 0;
647 } catch (RemoteException e) {
648 Log.e(TAG,"",e);
649 }
650 }
651
652 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800653 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800654 *
655 * <p>The connection may not be established right away, but will be
656 * completed when the remote device is available. A
657 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
658 * invoked when the connection state changes as a result of this function.
659 *
660 * <p>The autoConnect paramter determines whether to actively connect to
661 * the remote device, or rather passively scan and finalize the connection
662 * when the remote device is in range/available. Generally, the first ever
663 * connection to a device should be direct (autoConnect set to false) and
664 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800665 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800666 *
667 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
668 *
669 * @param device Remote device to connect to
670 * @param autoConnect Whether to directly connect to the remote device (false)
671 * or to automatically connect as soon as the remote
672 * device becomes available (true).
673 * @return true, if the connection attempt was initiated successfully
674 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800675 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
676 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
677 synchronized(mStateLock) {
678 if (mConnState != CONN_STATE_IDLE) {
679 throw new IllegalStateException("Not idle");
680 }
681 mConnState = CONN_STATE_CONNECTING;
682 }
683 if (!registerApp(callback)) {
684 synchronized(mStateLock) {
685 mConnState = CONN_STATE_IDLE;
686 }
687 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800688 return false;
689 }
690
Matthew Xieddf7e472013-03-01 18:41:02 -0800691 // the connection will continue after successful callback registration
692 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800693 return true;
694 }
695
Andre Eisenbachf46b21a2013-08-06 19:57:48 -0700696 /**
697 * Starts or stops sending of advertisement packages to listen for connection
698 * requests from a central devices.
699 *
700 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
701 *
702 * @param start Start or stop advertising
703 */
704 /*package*/ void listen(boolean start) {
705 if (DBG) Log.d(TAG, "listen() - start: " + start);
706 if (mService == null || mClientIf == 0) return;
707
708 try {
709 mService.clientListen(mClientIf, start);
710 } catch (RemoteException e) {
711 Log.e(TAG,"",e);
712 }
713 }
714
715 /**
716 * Sets the advertising data contained in the adv. response packet.
717 *
718 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
719 *
720 * @param advData true to set adv. data, false to set scan response
721 * @param includeName Inlucde the name in the adv. response
722 * @param includeTxPower Include TX power value
723 * @param minInterval Minimum desired scan interval (optional)
724 * @param maxInterval Maximum desired scan interval (optional)
725 * @param appearance The appearance flags for the device (optional)
726 * @param manufacturerData Manufacturer specific data including company ID (optional)
727 */
728 /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower,
729 Integer minInterval, Integer maxInterval,
730 Integer appearance, Byte[] manufacturerData) {
731 if (DBG) Log.d(TAG, "setAdvData()");
732 if (mService == null || mClientIf == 0) return;
733
734 byte[] data = new byte[0];
735 if (manufacturerData != null) {
736 data = new byte[manufacturerData.length];
737 for(int i = 0; i != manufacturerData.length; ++i) {
738 data[i] = manufacturerData[i];
739 }
740 }
741
742 try {
743 mService.setAdvData(mClientIf, !advData,
744 includeName, includeTxPower,
745 minInterval != null ? minInterval : 0,
746 maxInterval != null ? maxInterval : 0,
747 appearance != null ? appearance : 0, data);
748 } catch (RemoteException e) {
749 Log.e(TAG,"",e);
750 }
751 }
752
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800753 /**
754 * Disconnects an established connection, or cancels a connection attempt
755 * currently in progress.
756 *
757 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800758 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800759 public void disconnect() {
760 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800761 if (mService == null || mClientIf == 0) return;
762
763 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800764 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800765 } catch (RemoteException e) {
766 Log.e(TAG,"",e);
767 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700768 }
769
770 /**
771 * Connect back to remote device.
772 *
773 * <p>This method is used to re-connect to a remote device after the
774 * connection has been dropped. If the device is not in range, the
775 * re-connection will be triggered once the device is back in range.
776 *
777 * @return true, if the connection attempt was initiated successfully
778 */
779 public boolean connect() {
780 try {
781 mService.clientConnect(mClientIf, mDevice.getAddress(),
782 false); // autoConnect is inverse of "isDirect"
783 return true;
784 } catch (RemoteException e) {
785 Log.e(TAG,"",e);
786 return false;
787 }
788 }
789
790 /**
791 * Return the remote bluetooth device this GATT client targets to
792 *
793 * @return remote bluetooth device
794 */
795 public BluetoothDevice getDevice() {
796 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800797 }
798
799 /**
800 * Discovers services offered by a remote device as well as their
801 * characteristics and descriptors.
802 *
803 * <p>This is an asynchronous operation. Once service discovery is completed,
804 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
805 * triggered. If the discovery was successful, the remote services can be
806 * retrieved using the {@link #getServices} function.
807 *
808 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
809 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800810 * @return true, if the remote service discovery has been started
811 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800812 public boolean discoverServices() {
813 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800814 if (mService == null || mClientIf == 0) return false;
815
816 mServices.clear();
817
818 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800819 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800820 } catch (RemoteException e) {
821 Log.e(TAG,"",e);
822 return false;
823 }
824
825 return true;
826 }
827
828 /**
829 * Returns a list of GATT services offered by the remote device.
830 *
831 * <p>This function requires that service discovery has been completed
832 * for the given device.
833 *
834 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
835 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800836 * @return List of services on the remote device. Returns an empty list
837 * if service discovery has not yet been performed.
838 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800839 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800840 List<BluetoothGattService> result =
841 new ArrayList<BluetoothGattService>();
842
843 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800844 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800845 result.add(service);
846 }
847 }
848
849 return result;
850 }
851
852 /**
853 * Returns a {@link BluetoothGattService}, if the requested UUID is
854 * supported by the remote device.
855 *
856 * <p>This function requires that service discovery has been completed
857 * for the given device.
858 *
859 * <p>If multiple instances of the same service (as identified by UUID)
860 * exist, the first instance of the service is returned.
861 *
862 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
863 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800864 * @param uuid UUID of the requested service
865 * @return BluetoothGattService if supported, or null if the requested
866 * service is not offered by the remote device.
867 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800868 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800869 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800870 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800871 service.getUuid().equals(uuid)) {
872 return service;
873 }
874 }
875
876 return null;
877 }
878
879 /**
880 * Reads the requested characteristic from the associated remote device.
881 *
882 * <p>This is an asynchronous operation. The result of the read operation
883 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
884 * callback.
885 *
886 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
887 *
888 * @param characteristic Characteristic to read from the remote device
889 * @return true, if the read operation was initiated successfully
890 */
891 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
892 if ((characteristic.getProperties() &
893 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
894
895 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
896 if (mService == null || mClientIf == 0) return false;
897
898 BluetoothGattService service = characteristic.getService();
899 if (service == null) return false;
900
901 BluetoothDevice device = service.getDevice();
902 if (device == null) return false;
903
904 try {
905 mService.readCharacteristic(mClientIf, device.getAddress(),
906 service.getType(), service.getInstanceId(),
907 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
908 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
909 } catch (RemoteException e) {
910 Log.e(TAG,"",e);
911 return false;
912 }
913
914 return true;
915 }
916
917 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800918 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800919 *
920 * <p>Once the write operation has been completed, the
921 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
922 * reporting the result of the operation.
923 *
924 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
925 *
926 * @param characteristic Characteristic to write on the remote device
927 * @return true, if the write operation was initiated successfully
928 */
929 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
930 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
931 && (characteristic.getProperties() &
932 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
933
934 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
935 if (mService == null || mClientIf == 0) return false;
936
937 BluetoothGattService service = characteristic.getService();
938 if (service == null) return false;
939
940 BluetoothDevice device = service.getDevice();
941 if (device == null) return false;
942
943 try {
944 mService.writeCharacteristic(mClientIf, device.getAddress(),
945 service.getType(), service.getInstanceId(),
946 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
947 new ParcelUuid(characteristic.getUuid()),
948 characteristic.getWriteType(), AUTHENTICATION_NONE,
949 characteristic.getValue());
950 } catch (RemoteException e) {
951 Log.e(TAG,"",e);
952 return false;
953 }
954
955 return true;
956 }
957
958 /**
959 * Reads the value for a given descriptor from the associated remote device.
960 *
961 * <p>Once the read operation has been completed, the
962 * {@link BluetoothGattCallback#onDescriptorRead} callback is
963 * triggered, signaling the result of the operation.
964 *
965 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
966 *
967 * @param descriptor Descriptor value to read from the remote device
968 * @return true, if the read operation was initiated successfully
969 */
970 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
971 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
972 if (mService == null || mClientIf == 0) return false;
973
974 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
975 if (characteristic == null) return false;
976
977 BluetoothGattService service = characteristic.getService();
978 if (service == null) return false;
979
980 BluetoothDevice device = service.getDevice();
981 if (device == null) return false;
982
983 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);
991 return false;
992 }
993
994 return true;
995 }
996
997 /**
998 * Write the value of a given descriptor to the associated remote device.
999 *
1000 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1001 * triggered to report the result of the write operation.
1002 *
1003 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1004 *
1005 * @param descriptor Descriptor to write to the associated remote device
1006 * @return true, if the write operation was initiated successfully
1007 */
1008 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1009 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
1010 if (mService == null || mClientIf == 0) return false;
1011
1012 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1013 if (characteristic == null) return false;
1014
1015 BluetoothGattService service = characteristic.getService();
1016 if (service == null) return false;
1017
1018 BluetoothDevice device = service.getDevice();
1019 if (device == null) return false;
1020
1021 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001022 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1023 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1024 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1025 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001026 characteristic.getWriteType(), AUTHENTICATION_NONE,
1027 descriptor.getValue());
1028 } catch (RemoteException e) {
1029 Log.e(TAG,"",e);
1030 return false;
1031 }
1032
1033 return true;
1034 }
1035
1036 /**
1037 * Initiates a reliable write transaction for a given remote device.
1038 *
1039 * <p>Once a reliable write transaction has been initiated, all calls
1040 * to {@link #writeCharacteristic} are sent to the remote device for
1041 * verification and queued up for atomic execution. The application will
1042 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1043 * in response to every {@link #writeCharacteristic} call and is responsible
1044 * for verifying if the value has been transmitted accurately.
1045 *
1046 * <p>After all characteristics have been queued up and verified,
1047 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1048 * was not written correctly, calling {@link #abortReliableWrite} will
1049 * cancel the current transaction without commiting any values on the
1050 * remote device.
1051 *
1052 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1053 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001054 * @return true, if the reliable write transaction has been initiated
1055 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001056 public boolean beginReliableWrite() {
1057 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001058 if (mService == null || mClientIf == 0) return false;
1059
1060 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001061 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001062 } catch (RemoteException e) {
1063 Log.e(TAG,"",e);
1064 return false;
1065 }
1066
1067 return true;
1068 }
1069
1070 /**
1071 * Executes a reliable write transaction for a given remote device.
1072 *
1073 * <p>This function will commit all queued up characteristic write
1074 * operations for a given remote device.
1075 *
1076 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1077 * invoked to indicate whether the transaction has been executed correctly.
1078 *
1079 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1080 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001081 * @return true, if the request to execute the transaction has been sent
1082 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001083 public boolean executeReliableWrite() {
1084 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001085 if (mService == null || mClientIf == 0) return false;
1086
1087 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001088 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001089 } catch (RemoteException e) {
1090 Log.e(TAG,"",e);
1091 return false;
1092 }
1093
1094 return true;
1095 }
1096
1097 /**
1098 * Cancels a reliable write transaction for a given device.
1099 *
1100 * <p>Calling this function will discard all queued characteristic write
1101 * operations for a given remote device.
1102 *
1103 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001104 */
John Du48f8b5d2013-08-19 12:20:37 -07001105 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001106 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001107 if (mService == null || mClientIf == 0) return;
1108
1109 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001110 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001111 } catch (RemoteException e) {
1112 Log.e(TAG,"",e);
1113 }
1114 }
1115
1116 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001117 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001118 */
1119 public void abortReliableWrite(BluetoothDevice mDevice) {
1120 abortReliableWrite();
1121 }
1122
1123 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 * Enable or disable notifications/indications for a given characteristic.
1125 *
1126 * <p>Once notifications are enabled for a characteristic, a
1127 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1128 * triggered if the remote device indicates that the given characteristic
1129 * has changed.
1130 *
1131 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1132 *
1133 * @param characteristic The characteristic for which to enable notifications
1134 * @param enable Set to true to enable notifications/indications
1135 * @return true, if the requested notification status was set successfully
1136 */
1137 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1138 boolean enable) {
1139 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1140 + " enable: " + enable);
1141 if (mService == null || mClientIf == 0) return false;
1142
1143 BluetoothGattService service = characteristic.getService();
1144 if (service == null) return false;
1145
1146 BluetoothDevice device = service.getDevice();
1147 if (device == null) return false;
1148
1149 try {
1150 mService.registerForNotification(mClientIf, device.getAddress(),
1151 service.getType(), service.getInstanceId(),
1152 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1153 new ParcelUuid(characteristic.getUuid()),
1154 enable);
1155 } catch (RemoteException e) {
1156 Log.e(TAG,"",e);
1157 return false;
1158 }
1159
1160 return true;
1161 }
1162
1163 /**
1164 * Clears the internal cache and forces a refresh of the services from the
1165 * remote device.
1166 * @hide
1167 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001168 public boolean refresh() {
1169 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001170 if (mService == null || mClientIf == 0) return false;
1171
1172 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001173 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001174 } catch (RemoteException e) {
1175 Log.e(TAG,"",e);
1176 return false;
1177 }
1178
1179 return true;
1180 }
1181
1182 /**
1183 * Read the RSSI for a connected remote device.
1184 *
1185 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1186 * invoked when the RSSI value has been read.
1187 *
1188 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1189 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001190 * @return true, if the RSSI value has been requested successfully
1191 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001192 public boolean readRemoteRssi() {
1193 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001194 if (mService == null || mClientIf == 0) return false;
1195
1196 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001197 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001198 } catch (RemoteException e) {
1199 Log.e(TAG,"",e);
1200 return false;
1201 }
1202
1203 return true;
1204 }
1205
1206 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001207 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1208 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001209 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001210 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001211 */
1212 @Override
1213 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001214 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001215 }
1216
1217 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001218 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1219 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001220 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001221 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001222 */
1223 @Override
1224 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001225 throw new UnsupportedOperationException
1226 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001227 }
1228
1229 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001230 * Not supported - please use
1231 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1232 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001234 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001235 */
1236 @Override
1237 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001238 throw new UnsupportedOperationException
1239 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001240 }
1241}