blob: e3820a2e2f2eb16db75ccf8a2b75cf934ce2f93f [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;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080021import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.os.ParcelUuid;
23import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080024import android.util.Log;
25
26import java.util.ArrayList;
27import java.util.List;
28import java.util.UUID;
29
30/**
Matthew Xieddf7e472013-03-01 18:41:02 -080031 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080032 *
Matthew Xieddf7e472013-03-01 18:41:02 -080033 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080034 * with Bluetooth Smart or Smart Ready devices.
35 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080036 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070037 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080038 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
39 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080040 */
41public final class BluetoothGatt implements BluetoothProfile {
42 private static final String TAG = "BluetoothGatt";
43 private static final boolean DBG = true;
Matthew Xieddf7e472013-03-01 18:41:02 -080044 private static final boolean VDBG = true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045
Matthew Xieddf7e472013-03-01 18:41:02 -080046 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080047 private IBluetoothGatt mService;
48 private BluetoothGattCallback mCallback;
49 private int mClientIf;
50 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080051 private BluetoothDevice mDevice;
52 private boolean mAutoConnect;
53 private int mConnState;
54 private final Object mStateLock = new Object();
55
56 private static final int CONN_STATE_IDLE = 0;
57 private static final int CONN_STATE_CONNECTING = 1;
58 private static final int CONN_STATE_CONNECTED = 2;
59 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070060 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080061
62 private List<BluetoothGattService> mServices;
63
Matthew Xieddf7e472013-03-01 18:41:02 -080064 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065 public static final int GATT_SUCCESS = 0;
66
Matthew Xieddf7e472013-03-01 18:41:02 -080067 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080068 public static final int GATT_READ_NOT_PERMITTED = 0x2;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
72
73 /** Insufficient authentication for a given operation */
74 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
75
76 /** The given request is not supported */
77 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
78
79 /** Insufficient encryption for a given operation */
80 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
81
82 /** A read or write operation was requested with an invalid offset */
83 public static final int GATT_INVALID_OFFSET = 0x7;
84
85 /** A write operation exceeds the maximum length of the attribute */
86 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
87
Matthew Xie90ca8072013-05-28 21:06:50 +000088 /** A GATT operation failed, errors other than the above */
89 public static final int GATT_FAILURE = 0x101;
90
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080091 /**
92 * No authentication required.
93 * @hide
94 */
95 /*package*/ static final int AUTHENTICATION_NONE = 0;
96
97 /**
98 * Authentication requested; no man-in-the-middle protection required.
99 * @hide
100 */
101 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
102
103 /**
104 * Authentication with man-in-the-middle protection requested.
105 * @hide
106 */
107 /*package*/ static final int AUTHENTICATION_MITM = 2;
108
109 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800110 * Bluetooth GATT interface callbacks
111 */
112 private final IBluetoothGattCallback mBluetoothGattCallback =
113 new IBluetoothGattCallback.Stub() {
114 /**
115 * Application interface registered - app is ready to go
116 * @hide
117 */
118 public void onClientRegistered(int status, int clientIf) {
119 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
120 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800121 if (VDBG) {
122 synchronized(mStateLock) {
123 if (mConnState != CONN_STATE_CONNECTING) {
124 Log.e(TAG, "Bad connection state: " + mConnState);
125 }
126 }
127 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800128 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800129 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700130 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800131 BluetoothProfile.STATE_DISCONNECTED);
132 synchronized(mStateLock) {
133 mConnState = CONN_STATE_IDLE;
134 }
135 return;
136 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800138 mService.clientConnect(mClientIf, mDevice.getAddress(),
139 !mAutoConnect); // autoConnect is inverse of "isDirect"
140 } catch (RemoteException e) {
141 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800142 }
143 }
144
145 /**
146 * Client connection state changed
147 * @hide
148 */
149 public void onClientConnectionState(int status, int clientIf,
150 boolean connected, String address) {
151 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
152 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800153 if (!address.equals(mDevice.getAddress())) {
154 return;
155 }
156 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
157 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800158 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700159 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800160 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700161 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800162 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800163
164 synchronized(mStateLock) {
165 if (connected) {
166 mConnState = CONN_STATE_CONNECTED;
167 } else {
168 mConnState = CONN_STATE_IDLE;
169 }
170 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800171 }
172
173 /**
174 * Callback reporting an LE scan result.
175 * @hide
176 */
177 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800178 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800179 }
180
181 /**
182 * A new GATT service has been discovered.
183 * The service is added to the internal list and the search
184 * continues.
185 * @hide
186 */
187 public void onGetService(String address, int srvcType,
188 int srvcInstId, ParcelUuid srvcUuid) {
189 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800190 if (!address.equals(mDevice.getAddress())) {
191 return;
192 }
193 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800194 srvcInstId, srvcType));
195 }
196
197 /**
198 * An included service has been found durig GATT discovery.
199 * The included service is added to the respective parent.
200 * @hide
201 */
202 public void onGetIncludedService(String address, int srvcType,
203 int srvcInstId, ParcelUuid srvcUuid,
204 int inclSrvcType, int inclSrvcInstId,
205 ParcelUuid inclSrvcUuid) {
206 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
207 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
208
Matthew Xieddf7e472013-03-01 18:41:02 -0800209 if (!address.equals(mDevice.getAddress())) {
210 return;
211 }
212 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800213 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800214 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800215 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
216
217 if (service != null && includedService != null) {
218 service.addIncludedService(includedService);
219 }
220 }
221
222 /**
223 * A new GATT characteristic has been discovered.
224 * Add the new characteristic to the relevant service and continue
225 * the remote device inspection.
226 * @hide
227 */
228 public void onGetCharacteristic(String address, int srvcType,
229 int srvcInstId, ParcelUuid srvcUuid,
230 int charInstId, ParcelUuid charUuid,
231 int charProps) {
232 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
233 charUuid);
234
Matthew Xieddf7e472013-03-01 18:41:02 -0800235 if (!address.equals(mDevice.getAddress())) {
236 return;
237 }
238 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800239 srvcInstId, srvcType);
240 if (service != null) {
241 service.addCharacteristic(new BluetoothGattCharacteristic(
242 service, charUuid.getUuid(), charInstId, charProps, 0));
243 }
244 }
245
246 /**
247 * A new GATT descriptor has been discovered.
248 * Finally, add the descriptor to the related characteristic.
249 * This should conclude the remote device update.
250 * @hide
251 */
252 public void onGetDescriptor(String address, int srvcType,
253 int srvcInstId, ParcelUuid srvcUuid,
254 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700255 int descrInstId, ParcelUuid descUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800256 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
257
Matthew Xieddf7e472013-03-01 18:41:02 -0800258 if (!address.equals(mDevice.getAddress())) {
259 return;
260 }
261 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800262 srvcInstId, srvcType);
263 if (service == null) return;
264
265 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
266 charUuid.getUuid());
267 if (characteristic == null) return;
268
269 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700270 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800271 }
272
273 /**
274 * Remote search has been completed.
275 * The internal object structure should now reflect the state
276 * of the remote device database. Let the application know that
277 * we are done at this point.
278 * @hide
279 */
280 public void onSearchComplete(String address, int status) {
281 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800282 if (!address.equals(mDevice.getAddress())) {
283 return;
284 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800285 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700286 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800287 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700288 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800289 }
290 }
291
292 /**
293 * Remote characteristic has been read.
294 * Updates the internal value.
295 * @hide
296 */
297 public void onCharacteristicRead(String address, int status, int srvcType,
298 int srvcInstId, ParcelUuid srvcUuid,
299 int charInstId, ParcelUuid charUuid, byte[] value) {
300 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
301 + " UUID=" + charUuid + " Status=" + status);
302
Matthew Xieddf7e472013-03-01 18:41:02 -0800303 if (!address.equals(mDevice.getAddress())) {
304 return;
305 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
307 || status == GATT_INSUFFICIENT_ENCRYPTION)
308 && mAuthRetry == false) {
309 try {
310 mAuthRetry = true;
311 mService.readCharacteristic(mClientIf, address,
312 srvcType, srvcInstId, srvcUuid,
313 charInstId, charUuid, AUTHENTICATION_MITM);
314 return;
315 } catch (RemoteException e) {
316 Log.e(TAG,"",e);
317 }
318 }
319
320 mAuthRetry = false;
321
Matthew Xieddf7e472013-03-01 18:41:02 -0800322 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800323 srvcInstId, srvcType);
324 if (service == null) return;
325
326 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
327 charUuid.getUuid(), charInstId);
328 if (characteristic == null) return;
329
330 if (status == 0) characteristic.setValue(value);
331
332 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700333 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800334 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700335 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800336 }
337 }
338
339 /**
340 * Characteristic has been written to the remote device.
341 * Let the app know how we did...
342 * @hide
343 */
344 public void onCharacteristicWrite(String address, int status, int srvcType,
345 int srvcInstId, ParcelUuid srvcUuid,
346 int charInstId, ParcelUuid charUuid) {
347 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
348 + " UUID=" + charUuid + " Status=" + status);
349
Matthew Xieddf7e472013-03-01 18:41:02 -0800350 if (!address.equals(mDevice.getAddress())) {
351 return;
352 }
353 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800354 srvcInstId, srvcType);
355 if (service == null) return;
356
357 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
358 charUuid.getUuid(), charInstId);
359 if (characteristic == null) return;
360
361 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
362 || status == GATT_INSUFFICIENT_ENCRYPTION)
363 && mAuthRetry == false) {
364 try {
365 mAuthRetry = true;
366 mService.writeCharacteristic(mClientIf, address,
367 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
368 characteristic.getWriteType(), AUTHENTICATION_MITM,
369 characteristic.getValue());
370 return;
371 } catch (RemoteException e) {
372 Log.e(TAG,"",e);
373 }
374 }
375
376 mAuthRetry = false;
377
378 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700379 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800380 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700381 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800382 }
383 }
384
385 /**
386 * Remote characteristic has been updated.
387 * Updates the internal value.
388 * @hide
389 */
390 public void onNotify(String address, int srvcType,
391 int srvcInstId, ParcelUuid srvcUuid,
392 int charInstId, ParcelUuid charUuid,
393 byte[] value) {
394 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
395
Matthew Xieddf7e472013-03-01 18:41:02 -0800396 if (!address.equals(mDevice.getAddress())) {
397 return;
398 }
399 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800400 srvcInstId, srvcType);
401 if (service == null) return;
402
403 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
404 charUuid.getUuid(), charInstId);
405 if (characteristic == null) return;
406
407 characteristic.setValue(value);
408
409 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700410 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800411 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700412 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800413 }
414 }
415
416 /**
417 * Descriptor has been read.
418 * @hide
419 */
420 public void onDescriptorRead(String address, int status, int srvcType,
421 int srvcInstId, ParcelUuid srvcUuid,
422 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700423 int descrInstId, ParcelUuid descrUuid,
424 byte[] value) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800425 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
426
Matthew Xieddf7e472013-03-01 18:41:02 -0800427 if (!address.equals(mDevice.getAddress())) {
428 return;
429 }
430 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800431 srvcInstId, srvcType);
432 if (service == null) return;
433
434 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
435 charUuid.getUuid(), charInstId);
436 if (characteristic == null) return;
437
438 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700439 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440 if (descriptor == null) return;
441
442 if (status == 0) descriptor.setValue(value);
443
444 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
445 || status == GATT_INSUFFICIENT_ENCRYPTION)
446 && mAuthRetry == false) {
447 try {
448 mAuthRetry = true;
449 mService.readDescriptor(mClientIf, address,
450 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700451 descrInstId, descrUuid, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800452 } catch (RemoteException e) {
453 Log.e(TAG,"",e);
454 }
455 }
456
457 mAuthRetry = true;
458
459 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700460 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800461 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700462 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800463 }
464 }
465
466 /**
467 * Descriptor write operation complete.
468 * @hide
469 */
470 public void onDescriptorWrite(String address, int status, int srvcType,
471 int srvcInstId, ParcelUuid srvcUuid,
472 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700473 int descrInstId, ParcelUuid descrUuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800474 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
475
Matthew Xieddf7e472013-03-01 18:41:02 -0800476 if (!address.equals(mDevice.getAddress())) {
477 return;
478 }
479 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800480 srvcInstId, srvcType);
481 if (service == null) return;
482
483 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
484 charUuid.getUuid(), charInstId);
485 if (characteristic == null) return;
486
487 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700488 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800489 if (descriptor == null) return;
490
491 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
492 || status == GATT_INSUFFICIENT_ENCRYPTION)
493 && mAuthRetry == false) {
494 try {
495 mAuthRetry = true;
496 mService.writeDescriptor(mClientIf, address,
497 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700498 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800499 AUTHENTICATION_MITM, descriptor.getValue());
500 } catch (RemoteException e) {
501 Log.e(TAG,"",e);
502 }
503 }
504
505 mAuthRetry = false;
506
507 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700508 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800509 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700510 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800511 }
512 }
513
514 /**
515 * Prepared write transaction completed (or aborted)
516 * @hide
517 */
518 public void onExecuteWrite(String address, int status) {
519 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
520 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800521 if (!address.equals(mDevice.getAddress())) {
522 return;
523 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800524 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700525 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800526 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700527 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800528 }
529 }
530
531 /**
532 * Remote device RSSI has been read
533 * @hide
534 */
535 public void onReadRemoteRssi(String address, int rssi, int status) {
536 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
537 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800538 if (!address.equals(mDevice.getAddress())) {
539 return;
540 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800541 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700542 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, 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
Matthew Xieddf7e472013-03-01 18:41:02 -0800549 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800550 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800551 mService = iGatt;
552 mDevice = device;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800553 mServices = new ArrayList<BluetoothGattService>();
554
Matthew Xieddf7e472013-03-01 18:41:02 -0800555 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800556 }
557
558 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700559 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700560 *
561 * Application should call this method as early as possible after it is done with
562 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800563 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700564 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800565 if (DBG) Log.d(TAG, "close()");
566
567 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700568 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800569 }
570
571 /**
572 * Returns a service by UUID, instance and type.
573 * @hide
574 */
575 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
576 int instanceId, int type) {
577 for(BluetoothGattService svc : mServices) {
578 if (svc.getDevice().equals(device) &&
579 svc.getType() == type &&
580 svc.getInstanceId() == instanceId &&
581 svc.getUuid().equals(uuid)) {
582 return svc;
583 }
584 }
585 return null;
586 }
587
588
589 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800590 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800591 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800592 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
593 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800594 *
595 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
596 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800597 * @param callback GATT callback handler that will receive asynchronous callbacks.
598 * @return If true, the callback will be called to notify success or failure,
599 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800600 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800601 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800602 if (DBG) Log.d(TAG, "registerApp()");
603 if (mService == null) return false;
604
605 mCallback = callback;
606 UUID uuid = UUID.randomUUID();
607 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
608
609 try {
610 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
611 } catch (RemoteException e) {
612 Log.e(TAG,"",e);
613 return false;
614 }
615
616 return true;
617 }
618
619 /**
620 * Unregister the current application and callbacks.
621 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800622 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800623 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
624 if (mService == null || mClientIf == 0) return;
625
626 try {
627 mCallback = null;
628 mService.unregisterClient(mClientIf);
629 mClientIf = 0;
630 } catch (RemoteException e) {
631 Log.e(TAG,"",e);
632 }
633 }
634
635 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800636 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800637 *
638 * <p>The connection may not be established right away, but will be
639 * completed when the remote device is available. A
640 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
641 * invoked when the connection state changes as a result of this function.
642 *
643 * <p>The autoConnect paramter determines whether to actively connect to
644 * the remote device, or rather passively scan and finalize the connection
645 * when the remote device is in range/available. Generally, the first ever
646 * connection to a device should be direct (autoConnect set to false) and
647 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800648 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800649 *
650 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
651 *
652 * @param device Remote device to connect to
653 * @param autoConnect Whether to directly connect to the remote device (false)
654 * or to automatically connect as soon as the remote
655 * device becomes available (true).
656 * @return true, if the connection attempt was initiated successfully
657 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800658 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
659 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
660 synchronized(mStateLock) {
661 if (mConnState != CONN_STATE_IDLE) {
662 throw new IllegalStateException("Not idle");
663 }
664 mConnState = CONN_STATE_CONNECTING;
665 }
666 if (!registerApp(callback)) {
667 synchronized(mStateLock) {
668 mConnState = CONN_STATE_IDLE;
669 }
670 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800671 return false;
672 }
673
Matthew Xieddf7e472013-03-01 18:41:02 -0800674 // the connection will continue after successful callback registration
675 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800676 return true;
677 }
678
679 /**
680 * Disconnects an established connection, or cancels a connection attempt
681 * currently in progress.
682 *
683 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800684 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800685 public void disconnect() {
686 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800687 if (mService == null || mClientIf == 0) return;
688
689 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800690 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800691 } catch (RemoteException e) {
692 Log.e(TAG,"",e);
693 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700694 }
695
696 /**
697 * Connect back to remote device.
698 *
699 * <p>This method is used to re-connect to a remote device after the
700 * connection has been dropped. If the device is not in range, the
701 * re-connection will be triggered once the device is back in range.
702 *
703 * @return true, if the connection attempt was initiated successfully
704 */
705 public boolean connect() {
706 try {
707 mService.clientConnect(mClientIf, mDevice.getAddress(),
708 false); // autoConnect is inverse of "isDirect"
709 return true;
710 } catch (RemoteException e) {
711 Log.e(TAG,"",e);
712 return false;
713 }
714 }
715
716 /**
717 * Return the remote bluetooth device this GATT client targets to
718 *
719 * @return remote bluetooth device
720 */
721 public BluetoothDevice getDevice() {
722 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800723 }
724
725 /**
726 * Discovers services offered by a remote device as well as their
727 * characteristics and descriptors.
728 *
729 * <p>This is an asynchronous operation. Once service discovery is completed,
730 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
731 * triggered. If the discovery was successful, the remote services can be
732 * retrieved using the {@link #getServices} function.
733 *
734 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
735 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800736 * @return true, if the remote service discovery has been started
737 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800738 public boolean discoverServices() {
739 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800740 if (mService == null || mClientIf == 0) return false;
741
742 mServices.clear();
743
744 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800745 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800746 } catch (RemoteException e) {
747 Log.e(TAG,"",e);
748 return false;
749 }
750
751 return true;
752 }
753
754 /**
755 * Returns a list of GATT services offered by the remote device.
756 *
757 * <p>This function requires that service discovery has been completed
758 * for the given device.
759 *
760 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
761 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800762 * @return List of services on the remote device. Returns an empty list
763 * if service discovery has not yet been performed.
764 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800765 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800766 List<BluetoothGattService> result =
767 new ArrayList<BluetoothGattService>();
768
769 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800770 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800771 result.add(service);
772 }
773 }
774
775 return result;
776 }
777
778 /**
779 * Returns a {@link BluetoothGattService}, if the requested UUID is
780 * supported by the remote device.
781 *
782 * <p>This function requires that service discovery has been completed
783 * for the given device.
784 *
785 * <p>If multiple instances of the same service (as identified by UUID)
786 * exist, the first instance of the service is returned.
787 *
788 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
789 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 * @param uuid UUID of the requested service
791 * @return BluetoothGattService if supported, or null if the requested
792 * service is not offered by the remote device.
793 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800794 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800795 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800796 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800797 service.getUuid().equals(uuid)) {
798 return service;
799 }
800 }
801
802 return null;
803 }
804
805 /**
806 * Reads the requested characteristic from the associated remote device.
807 *
808 * <p>This is an asynchronous operation. The result of the read operation
809 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
810 * callback.
811 *
812 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
813 *
814 * @param characteristic Characteristic to read from the remote device
815 * @return true, if the read operation was initiated successfully
816 */
817 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
818 if ((characteristic.getProperties() &
819 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
820
821 if (DBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
822 if (mService == null || mClientIf == 0) return false;
823
824 BluetoothGattService service = characteristic.getService();
825 if (service == null) return false;
826
827 BluetoothDevice device = service.getDevice();
828 if (device == null) return false;
829
830 try {
831 mService.readCharacteristic(mClientIf, device.getAddress(),
832 service.getType(), service.getInstanceId(),
833 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
834 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
835 } catch (RemoteException e) {
836 Log.e(TAG,"",e);
837 return false;
838 }
839
840 return true;
841 }
842
843 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800844 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800845 *
846 * <p>Once the write operation has been completed, the
847 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
848 * reporting the result of the operation.
849 *
850 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
851 *
852 * @param characteristic Characteristic to write on the remote device
853 * @return true, if the write operation was initiated successfully
854 */
855 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
856 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
857 && (characteristic.getProperties() &
858 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
859
860 if (DBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
861 if (mService == null || mClientIf == 0) return false;
862
863 BluetoothGattService service = characteristic.getService();
864 if (service == null) return false;
865
866 BluetoothDevice device = service.getDevice();
867 if (device == null) return false;
868
869 try {
870 mService.writeCharacteristic(mClientIf, device.getAddress(),
871 service.getType(), service.getInstanceId(),
872 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
873 new ParcelUuid(characteristic.getUuid()),
874 characteristic.getWriteType(), AUTHENTICATION_NONE,
875 characteristic.getValue());
876 } catch (RemoteException e) {
877 Log.e(TAG,"",e);
878 return false;
879 }
880
881 return true;
882 }
883
884 /**
885 * Reads the value for a given descriptor from the associated remote device.
886 *
887 * <p>Once the read operation has been completed, the
888 * {@link BluetoothGattCallback#onDescriptorRead} callback is
889 * triggered, signaling the result of the operation.
890 *
891 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
892 *
893 * @param descriptor Descriptor value to read from the remote device
894 * @return true, if the read operation was initiated successfully
895 */
896 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
897 if (DBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
898 if (mService == null || mClientIf == 0) return false;
899
900 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
901 if (characteristic == null) return false;
902
903 BluetoothGattService service = characteristic.getService();
904 if (service == null) return false;
905
906 BluetoothDevice device = service.getDevice();
907 if (device == null) return false;
908
909 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700910 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
911 service.getInstanceId(), new ParcelUuid(service.getUuid()),
912 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
913 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
914 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800915 } catch (RemoteException e) {
916 Log.e(TAG,"",e);
917 return false;
918 }
919
920 return true;
921 }
922
923 /**
924 * Write the value of a given descriptor to the associated remote device.
925 *
926 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
927 * triggered to report the result of the write operation.
928 *
929 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
930 *
931 * @param descriptor Descriptor to write to the associated remote device
932 * @return true, if the write operation was initiated successfully
933 */
934 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
935 if (DBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
936 if (mService == null || mClientIf == 0) return false;
937
938 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
939 if (characteristic == null) return false;
940
941 BluetoothGattService service = characteristic.getService();
942 if (service == null) return false;
943
944 BluetoothDevice device = service.getDevice();
945 if (device == null) return false;
946
947 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700948 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
949 service.getInstanceId(), new ParcelUuid(service.getUuid()),
950 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
951 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800952 characteristic.getWriteType(), AUTHENTICATION_NONE,
953 descriptor.getValue());
954 } catch (RemoteException e) {
955 Log.e(TAG,"",e);
956 return false;
957 }
958
959 return true;
960 }
961
962 /**
963 * Initiates a reliable write transaction for a given remote device.
964 *
965 * <p>Once a reliable write transaction has been initiated, all calls
966 * to {@link #writeCharacteristic} are sent to the remote device for
967 * verification and queued up for atomic execution. The application will
968 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
969 * in response to every {@link #writeCharacteristic} call and is responsible
970 * for verifying if the value has been transmitted accurately.
971 *
972 * <p>After all characteristics have been queued up and verified,
973 * {@link #executeReliableWrite} will execute all writes. If a characteristic
974 * was not written correctly, calling {@link #abortReliableWrite} will
975 * cancel the current transaction without commiting any values on the
976 * remote device.
977 *
978 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
979 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800980 * @return true, if the reliable write transaction has been initiated
981 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800982 public boolean beginReliableWrite() {
983 if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800984 if (mService == null || mClientIf == 0) return false;
985
986 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800987 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800988 } catch (RemoteException e) {
989 Log.e(TAG,"",e);
990 return false;
991 }
992
993 return true;
994 }
995
996 /**
997 * Executes a reliable write transaction for a given remote device.
998 *
999 * <p>This function will commit all queued up characteristic write
1000 * operations for a given remote device.
1001 *
1002 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1003 * invoked to indicate whether the transaction has been executed correctly.
1004 *
1005 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1006 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001007 * @return true, if the request to execute the transaction has been sent
1008 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001009 public boolean executeReliableWrite() {
1010 if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001011 if (mService == null || mClientIf == 0) return false;
1012
1013 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001014 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 } catch (RemoteException e) {
1016 Log.e(TAG,"",e);
1017 return false;
1018 }
1019
1020 return true;
1021 }
1022
1023 /**
1024 * Cancels a reliable write transaction for a given device.
1025 *
1026 * <p>Calling this function will discard all queued characteristic write
1027 * operations for a given remote device.
1028 *
1029 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001030 */
John Du48f8b5d2013-08-19 12:20:37 -07001031 public void abortReliableWrite() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001032 if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001033 if (mService == null || mClientIf == 0) return;
1034
1035 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001036 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001037 } catch (RemoteException e) {
1038 Log.e(TAG,"",e);
1039 }
1040 }
1041
1042 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001043 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001044 */
1045 public void abortReliableWrite(BluetoothDevice mDevice) {
1046 abortReliableWrite();
1047 }
1048
1049 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001050 * Enable or disable notifications/indications for a given characteristic.
1051 *
1052 * <p>Once notifications are enabled for a characteristic, a
1053 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1054 * triggered if the remote device indicates that the given characteristic
1055 * has changed.
1056 *
1057 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1058 *
1059 * @param characteristic The characteristic for which to enable notifications
1060 * @param enable Set to true to enable notifications/indications
1061 * @return true, if the requested notification status was set successfully
1062 */
1063 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1064 boolean enable) {
1065 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1066 + " enable: " + enable);
1067 if (mService == null || mClientIf == 0) return false;
1068
1069 BluetoothGattService service = characteristic.getService();
1070 if (service == null) return false;
1071
1072 BluetoothDevice device = service.getDevice();
1073 if (device == null) return false;
1074
1075 try {
1076 mService.registerForNotification(mClientIf, device.getAddress(),
1077 service.getType(), service.getInstanceId(),
1078 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1079 new ParcelUuid(characteristic.getUuid()),
1080 enable);
1081 } catch (RemoteException e) {
1082 Log.e(TAG,"",e);
1083 return false;
1084 }
1085
1086 return true;
1087 }
1088
1089 /**
1090 * Clears the internal cache and forces a refresh of the services from the
1091 * remote device.
1092 * @hide
1093 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001094 public boolean refresh() {
1095 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001096 if (mService == null || mClientIf == 0) return false;
1097
1098 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001099 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001100 } catch (RemoteException e) {
1101 Log.e(TAG,"",e);
1102 return false;
1103 }
1104
1105 return true;
1106 }
1107
1108 /**
1109 * Read the RSSI for a connected remote device.
1110 *
1111 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1112 * invoked when the RSSI value has been read.
1113 *
1114 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1115 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001116 * @return true, if the RSSI value has been requested successfully
1117 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001118 public boolean readRemoteRssi() {
1119 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001120 if (mService == null || mClientIf == 0) return false;
1121
1122 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001123 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 } catch (RemoteException e) {
1125 Log.e(TAG,"",e);
1126 return false;
1127 }
1128
1129 return true;
1130 }
1131
1132 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001133 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1134 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001135 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001136 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001137 */
1138 @Override
1139 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001140 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001141 }
1142
1143 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001144 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1145 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001146 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001147 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001148 */
1149 @Override
1150 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001151 throw new UnsupportedOperationException
1152 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001153 }
1154
1155 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001156 * Not supported - please use
1157 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1158 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001159 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001160 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001161 */
1162 @Override
1163 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001164 throw new UnsupportedOperationException
1165 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001166 }
1167}