blob: 1fe43ec77c8761016ec3acda4c4889c5c907e6f3 [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
Wei Wang9fb17912014-07-01 15:10:06 -070019import android.bluetooth.le.ScanResult;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080020import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080021import android.os.ParcelUuid;
22import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080023import android.util.Log;
24
25import java.util.ArrayList;
26import java.util.List;
27import java.util.UUID;
28
29/**
Matthew Xieddf7e472013-03-01 18:41:02 -080030 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080031 *
Matthew Xieddf7e472013-03-01 18:41:02 -080032 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080033 * with Bluetooth Smart or Smart Ready devices.
34 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080035 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070036 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080037 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
38 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080039 */
40public final class BluetoothGatt implements BluetoothProfile {
41 private static final String TAG = "BluetoothGatt";
42 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070043 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044
Matthew Xieddf7e472013-03-01 18:41:02 -080045 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080046 private IBluetoothGatt mService;
47 private BluetoothGattCallback mCallback;
48 private int mClientIf;
49 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080050 private BluetoothDevice mDevice;
51 private boolean mAutoConnect;
52 private int mConnState;
53 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070054 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070055 private int mTransport;
Matthew Xieddf7e472013-03-01 18:41:02 -080056
57 private static final int CONN_STATE_IDLE = 0;
58 private static final int CONN_STATE_CONNECTING = 1;
59 private static final int CONN_STATE_CONNECTED = 2;
60 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070061 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080062
63 private List<BluetoothGattService> mServices;
64
Matthew Xieddf7e472013-03-01 18:41:02 -080065 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080066 public static final int GATT_SUCCESS = 0;
67
Matthew Xieddf7e472013-03-01 18:41:02 -080068 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080069 public static final int GATT_READ_NOT_PERMITTED = 0x2;
70
Matthew Xieddf7e472013-03-01 18:41:02 -080071 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080072 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
73
74 /** Insufficient authentication for a given operation */
75 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
76
77 /** The given request is not supported */
78 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
79
80 /** Insufficient encryption for a given operation */
81 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
82
83 /** A read or write operation was requested with an invalid offset */
84 public static final int GATT_INVALID_OFFSET = 0x7;
85
86 /** A write operation exceeds the maximum length of the attribute */
87 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
88
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070089 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070090 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
91
Matthew Xie90ca8072013-05-28 21:06:50 +000092 /** A GATT operation failed, errors other than the above */
93 public static final int GATT_FAILURE = 0x101;
94
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080095 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070096 * Connection paramter update - Use the connection paramters recommended by the
97 * Bluetooth SIG. This is the default value if no connection parameter update
98 * is requested.
99 */
100 public static final int GATT_CONNECTION_BALANCED = 0;
101
102 /**
103 * Connection paramter update - Request a high priority, low latency connection.
104 * An application should only request high priority connection paramters to transfer
105 * large amounts of data over LE quickly. Once the transfer is complete, the application
106 * should request {@link BluetoothGatt#GATT_CONNECTION_BALANCED} connectoin parameters
107 * to reduce energy use.
108 */
109 public static final int GATT_CONNECTION_HIGH_PRIORITY = 1;
110
111 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
112 public static final int GATT_CONNECTION_LOW_POWER = 2;
113
114 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800115 * No authentication required.
116 * @hide
117 */
118 /*package*/ static final int AUTHENTICATION_NONE = 0;
119
120 /**
121 * Authentication requested; no man-in-the-middle protection required.
122 * @hide
123 */
124 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
125
126 /**
127 * Authentication with man-in-the-middle protection requested.
128 * @hide
129 */
130 /*package*/ static final int AUTHENTICATION_MITM = 2;
131
132 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800133 * Bluetooth GATT interface callbacks
134 */
135 private final IBluetoothGattCallback mBluetoothGattCallback =
136 new IBluetoothGattCallback.Stub() {
137 /**
138 * Application interface registered - app is ready to go
139 * @hide
140 */
141 public void onClientRegistered(int status, int clientIf) {
142 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
143 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800144 if (VDBG) {
145 synchronized(mStateLock) {
146 if (mConnState != CONN_STATE_CONNECTING) {
147 Log.e(TAG, "Bad connection state: " + mConnState);
148 }
149 }
150 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800151 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800152 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700153 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800154 BluetoothProfile.STATE_DISCONNECTED);
155 synchronized(mStateLock) {
156 mConnState = CONN_STATE_IDLE;
157 }
158 return;
159 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800160 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800161 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700162 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800163 } catch (RemoteException e) {
164 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800165 }
166 }
167
168 /**
169 * Client connection state changed
170 * @hide
171 */
172 public void onClientConnectionState(int status, int clientIf,
173 boolean connected, String address) {
174 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
175 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800176 if (!address.equals(mDevice.getAddress())) {
177 return;
178 }
179 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
180 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800181 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700182 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800183 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700184 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800185 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800186
187 synchronized(mStateLock) {
188 if (connected) {
189 mConnState = CONN_STATE_CONNECTED;
190 } else {
191 mConnState = CONN_STATE_IDLE;
192 }
193 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700194
195 synchronized(mDeviceBusy) {
196 mDeviceBusy = false;
197 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800198 }
199
200 /**
201 * Callback reporting an LE scan result.
202 * @hide
203 */
204 public void onScanResult(String address, int rssi, byte[] advData) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800205 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800206 }
207
208 /**
209 * A new GATT service has been discovered.
210 * The service is added to the internal list and the search
211 * continues.
212 * @hide
213 */
214 public void onGetService(String address, int srvcType,
215 int srvcInstId, ParcelUuid srvcUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700216 if (VDBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
Matthew Xieddf7e472013-03-01 18:41:02 -0800217 if (!address.equals(mDevice.getAddress())) {
218 return;
219 }
220 mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800221 srvcInstId, srvcType));
222 }
223
224 /**
225 * An included service has been found durig GATT discovery.
226 * The included service is added to the respective parent.
227 * @hide
228 */
229 public void onGetIncludedService(String address, int srvcType,
230 int srvcInstId, ParcelUuid srvcUuid,
231 int inclSrvcType, int inclSrvcInstId,
232 ParcelUuid inclSrvcUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700233 if (VDBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800234 + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
235
Matthew Xieddf7e472013-03-01 18:41:02 -0800236 if (!address.equals(mDevice.getAddress())) {
237 return;
238 }
239 BluetoothGattService service = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800240 srvcUuid.getUuid(), srvcInstId, srvcType);
Matthew Xieddf7e472013-03-01 18:41:02 -0800241 BluetoothGattService includedService = getService(mDevice,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800242 inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
243
244 if (service != null && includedService != null) {
245 service.addIncludedService(includedService);
246 }
247 }
248
249 /**
250 * A new GATT characteristic has been discovered.
251 * Add the new characteristic to the relevant service and continue
252 * the remote device inspection.
253 * @hide
254 */
255 public void onGetCharacteristic(String address, int srvcType,
256 int srvcInstId, ParcelUuid srvcUuid,
257 int charInstId, ParcelUuid charUuid,
258 int charProps) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700259 if (VDBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800260 charUuid);
261
Matthew Xieddf7e472013-03-01 18:41:02 -0800262 if (!address.equals(mDevice.getAddress())) {
263 return;
264 }
265 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800266 srvcInstId, srvcType);
267 if (service != null) {
268 service.addCharacteristic(new BluetoothGattCharacteristic(
269 service, charUuid.getUuid(), charInstId, charProps, 0));
270 }
271 }
272
273 /**
274 * A new GATT descriptor has been discovered.
275 * Finally, add the descriptor to the related characteristic.
276 * This should conclude the remote device update.
277 * @hide
278 */
279 public void onGetDescriptor(String address, int srvcType,
280 int srvcInstId, ParcelUuid srvcUuid,
281 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700282 int descrInstId, ParcelUuid descUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700283 if (VDBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800284
Matthew Xieddf7e472013-03-01 18:41:02 -0800285 if (!address.equals(mDevice.getAddress())) {
286 return;
287 }
288 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800289 srvcInstId, srvcType);
290 if (service == null) return;
291
292 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
Mike J. Chen2975c682014-06-24 10:19:45 -0700293 charUuid.getUuid(), charInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800294 if (characteristic == null) return;
295
296 characteristic.addDescriptor(new BluetoothGattDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700297 characteristic, descUuid.getUuid(), descrInstId, 0));
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800298 }
299
300 /**
301 * Remote search has been completed.
302 * The internal object structure should now reflect the state
303 * of the remote device database. Let the application know that
304 * we are done at this point.
305 * @hide
306 */
307 public void onSearchComplete(String address, int status) {
308 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800309 if (!address.equals(mDevice.getAddress())) {
310 return;
311 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800312 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700313 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800314 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700315 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 }
317 }
318
319 /**
320 * Remote characteristic has been read.
321 * Updates the internal value.
322 * @hide
323 */
324 public void onCharacteristicRead(String address, int status, int srvcType,
325 int srvcInstId, ParcelUuid srvcUuid,
326 int charInstId, ParcelUuid charUuid, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700327 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800328 + " UUID=" + charUuid + " Status=" + status);
329
Matthew Xieddf7e472013-03-01 18:41:02 -0800330 if (!address.equals(mDevice.getAddress())) {
331 return;
332 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700333
334 synchronized(mDeviceBusy) {
335 mDeviceBusy = false;
336 }
337
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800338 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
339 || status == GATT_INSUFFICIENT_ENCRYPTION)
340 && mAuthRetry == false) {
341 try {
342 mAuthRetry = true;
343 mService.readCharacteristic(mClientIf, address,
344 srvcType, srvcInstId, srvcUuid,
345 charInstId, charUuid, AUTHENTICATION_MITM);
346 return;
347 } catch (RemoteException e) {
348 Log.e(TAG,"",e);
349 }
350 }
351
352 mAuthRetry = false;
353
Matthew Xieddf7e472013-03-01 18:41:02 -0800354 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800355 srvcInstId, srvcType);
356 if (service == null) return;
357
358 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
359 charUuid.getUuid(), charInstId);
360 if (characteristic == null) return;
361
362 if (status == 0) characteristic.setValue(value);
363
364 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700365 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800366 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700367 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800368 }
369 }
370
371 /**
372 * Characteristic has been written to the remote device.
373 * Let the app know how we did...
374 * @hide
375 */
376 public void onCharacteristicWrite(String address, int status, int srvcType,
377 int srvcInstId, ParcelUuid srvcUuid,
378 int charInstId, ParcelUuid charUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700379 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800380 + " UUID=" + charUuid + " Status=" + status);
381
Matthew Xieddf7e472013-03-01 18:41:02 -0800382 if (!address.equals(mDevice.getAddress())) {
383 return;
384 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700385
386 synchronized(mDeviceBusy) {
387 mDeviceBusy = false;
388 }
389
Matthew Xieddf7e472013-03-01 18:41:02 -0800390 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800391 srvcInstId, srvcType);
392 if (service == null) return;
393
394 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
395 charUuid.getUuid(), charInstId);
396 if (characteristic == null) return;
397
398 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
399 || status == GATT_INSUFFICIENT_ENCRYPTION)
400 && mAuthRetry == false) {
401 try {
402 mAuthRetry = true;
403 mService.writeCharacteristic(mClientIf, address,
404 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
405 characteristic.getWriteType(), AUTHENTICATION_MITM,
406 characteristic.getValue());
407 return;
408 } catch (RemoteException e) {
409 Log.e(TAG,"",e);
410 }
411 }
412
413 mAuthRetry = false;
414
415 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700416 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800417 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700418 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800419 }
420 }
421
422 /**
423 * Remote characteristic has been updated.
424 * Updates the internal value.
425 * @hide
426 */
427 public void onNotify(String address, int srvcType,
428 int srvcInstId, ParcelUuid srvcUuid,
429 int charInstId, ParcelUuid charUuid,
430 byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700431 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800432
Matthew Xieddf7e472013-03-01 18:41:02 -0800433 if (!address.equals(mDevice.getAddress())) {
434 return;
435 }
436 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800437 srvcInstId, srvcType);
438 if (service == null) return;
439
440 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
441 charUuid.getUuid(), charInstId);
442 if (characteristic == null) return;
443
444 characteristic.setValue(value);
445
446 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700447 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800448 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700449 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800450 }
451 }
452
453 /**
454 * Descriptor has been read.
455 * @hide
456 */
457 public void onDescriptorRead(String address, int status, int srvcType,
458 int srvcInstId, ParcelUuid srvcUuid,
459 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700460 int descrInstId, ParcelUuid descrUuid,
461 byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700462 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800463
Matthew Xieddf7e472013-03-01 18:41:02 -0800464 if (!address.equals(mDevice.getAddress())) {
465 return;
466 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700467
468 synchronized(mDeviceBusy) {
469 mDeviceBusy = false;
470 }
471
Matthew Xieddf7e472013-03-01 18:41:02 -0800472 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800473 srvcInstId, srvcType);
474 if (service == null) return;
475
476 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
477 charUuid.getUuid(), charInstId);
478 if (characteristic == null) return;
479
480 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700481 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800482 if (descriptor == null) return;
483
484 if (status == 0) descriptor.setValue(value);
485
486 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
487 || status == GATT_INSUFFICIENT_ENCRYPTION)
488 && mAuthRetry == false) {
489 try {
490 mAuthRetry = true;
491 mService.readDescriptor(mClientIf, address,
492 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700493 descrInstId, descrUuid, AUTHENTICATION_MITM);
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700494 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800495 } catch (RemoteException e) {
496 Log.e(TAG,"",e);
497 }
498 }
499
500 mAuthRetry = true;
501
502 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700503 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700505 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800506 }
507 }
508
509 /**
510 * Descriptor write operation complete.
511 * @hide
512 */
513 public void onDescriptorWrite(String address, int status, int srvcType,
514 int srvcInstId, ParcelUuid srvcUuid,
515 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700516 int descrInstId, ParcelUuid descrUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700517 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800518
Matthew Xieddf7e472013-03-01 18:41:02 -0800519 if (!address.equals(mDevice.getAddress())) {
520 return;
521 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700522
523 synchronized(mDeviceBusy) {
524 mDeviceBusy = false;
525 }
526
Matthew Xieddf7e472013-03-01 18:41:02 -0800527 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800528 srvcInstId, srvcType);
529 if (service == null) return;
530
531 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
532 charUuid.getUuid(), charInstId);
533 if (characteristic == null) return;
534
535 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700536 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800537 if (descriptor == null) return;
538
539 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
540 || status == GATT_INSUFFICIENT_ENCRYPTION)
541 && mAuthRetry == false) {
542 try {
543 mAuthRetry = true;
544 mService.writeDescriptor(mClientIf, address,
545 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700546 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800547 AUTHENTICATION_MITM, descriptor.getValue());
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700548 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800549 } catch (RemoteException e) {
550 Log.e(TAG,"",e);
551 }
552 }
553
554 mAuthRetry = false;
555
556 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700557 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800558 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700559 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800560 }
561 }
562
563 /**
564 * Prepared write transaction completed (or aborted)
565 * @hide
566 */
567 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700568 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800569 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800570 if (!address.equals(mDevice.getAddress())) {
571 return;
572 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700573
574 synchronized(mDeviceBusy) {
575 mDeviceBusy = false;
576 }
577
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800578 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700579 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700581 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 }
583 }
584
585 /**
586 * Remote device RSSI has been read
587 * @hide
588 */
589 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700590 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800591 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800592 if (!address.equals(mDevice.getAddress())) {
593 return;
594 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800595 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700596 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800597 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700598 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800599 }
600 }
Wei Wangf3055892014-03-11 22:22:41 -0700601
602 /**
603 * Advertise state change callback
604 * @hide
605 */
606 public void onAdvertiseStateChange(int state, int status) {
607 if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
608 + state + " status=" + status);
Wei Wangadf6aff2014-05-20 06:30:20 +0000609 }
610
611 /**
612 * @hide
613 */
614 @Override
615 public void onMultiAdvertiseCallback(int status) {
616 // no op.
617 }
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700618
619 /**
620 * Callback invoked when the MTU for a given connection changes
621 * @hide
622 */
623 public void onConfigureMTU(String address, int mtu, int status) {
624 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
625 " mtu=" + mtu + " status=" + status);
626 if (!address.equals(mDevice.getAddress())) {
627 return;
628 }
629 try {
630 mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status);
631 } catch (Exception ex) {
632 Log.w(TAG, "Unhandled exception in callback", ex);
633 }
Wei Wangf3055892014-03-11 22:22:41 -0700634 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700635
636 /**
637 * Callback indicating the remote device connection is congested.
638 * @hide
639 */
640 public void onConnectionCongested(String address, boolean congested) {
641 if (DBG) Log.d(TAG, "onConnectionCongested() - Device=" + address
642 + " congested=" + congested);
643 if (!address.equals(mDevice.getAddress())) return;
644 try {
645 mCallback.onConnectionCongested(BluetoothGatt.this, congested);
646 } catch (Exception ex) {
647 Log.w(TAG, "Unhandled exception in callback", ex);
648 }
649 }
Wei Wang9fb17912014-07-01 15:10:06 -0700650
651 @Override
652 public void onBatchScanResults(List<ScanResult> results) {
653 // no op
654 }
Prerepa Viswanadham8f2e74c2014-07-09 12:51:59 -0700655
656 /**
657 * @hide
658 */
659 @Override
660 public void onFoundOrLost(boolean onFound, String address, int rssi,
661 byte[] advData) {
662 // no op.
663 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800664 };
665
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700666 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
667 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800668 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800669 mService = iGatt;
670 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700671 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800672 mServices = new ArrayList<BluetoothGattService>();
673
Matthew Xieddf7e472013-03-01 18:41:02 -0800674 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800675 }
676
677 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700678 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700679 *
680 * Application should call this method as early as possible after it is done with
681 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700683 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800684 if (DBG) Log.d(TAG, "close()");
685
686 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700687 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800688 }
689
690 /**
691 * Returns a service by UUID, instance and type.
692 * @hide
693 */
694 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
695 int instanceId, int type) {
696 for(BluetoothGattService svc : mServices) {
697 if (svc.getDevice().equals(device) &&
698 svc.getType() == type &&
699 svc.getInstanceId() == instanceId &&
700 svc.getUuid().equals(uuid)) {
701 return svc;
702 }
703 }
704 return null;
705 }
706
707
708 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800709 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800710 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800711 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
712 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800713 *
714 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
715 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800716 * @param callback GATT callback handler that will receive asynchronous callbacks.
717 * @return If true, the callback will be called to notify success or failure,
718 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800719 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800720 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 if (DBG) Log.d(TAG, "registerApp()");
722 if (mService == null) return false;
723
724 mCallback = callback;
725 UUID uuid = UUID.randomUUID();
726 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
727
728 try {
729 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
730 } catch (RemoteException e) {
731 Log.e(TAG,"",e);
732 return false;
733 }
734
735 return true;
736 }
737
738 /**
739 * Unregister the current application and callbacks.
740 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800741 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800742 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
743 if (mService == null || mClientIf == 0) return;
744
745 try {
746 mCallback = null;
747 mService.unregisterClient(mClientIf);
748 mClientIf = 0;
749 } catch (RemoteException e) {
750 Log.e(TAG,"",e);
751 }
752 }
753
754 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800755 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800756 *
757 * <p>The connection may not be established right away, but will be
758 * completed when the remote device is available. A
759 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
760 * invoked when the connection state changes as a result of this function.
761 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700762 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800763 * the remote device, or rather passively scan and finalize the connection
764 * when the remote device is in range/available. Generally, the first ever
765 * connection to a device should be direct (autoConnect set to false) and
766 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800767 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800768 *
769 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
770 *
771 * @param device Remote device to connect to
772 * @param autoConnect Whether to directly connect to the remote device (false)
773 * or to automatically connect as soon as the remote
774 * device becomes available (true).
775 * @return true, if the connection attempt was initiated successfully
776 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800777 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
778 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
779 synchronized(mStateLock) {
780 if (mConnState != CONN_STATE_IDLE) {
781 throw new IllegalStateException("Not idle");
782 }
783 mConnState = CONN_STATE_CONNECTING;
784 }
785 if (!registerApp(callback)) {
786 synchronized(mStateLock) {
787 mConnState = CONN_STATE_IDLE;
788 }
789 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 return false;
791 }
792
Matthew Xieddf7e472013-03-01 18:41:02 -0800793 // the connection will continue after successful callback registration
794 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800795 return true;
796 }
797
798 /**
799 * Disconnects an established connection, or cancels a connection attempt
800 * currently in progress.
801 *
802 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800803 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800804 public void disconnect() {
805 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 if (mService == null || mClientIf == 0) return;
807
808 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800809 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800810 } catch (RemoteException e) {
811 Log.e(TAG,"",e);
812 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700813 }
814
815 /**
816 * Connect back to remote device.
817 *
818 * <p>This method is used to re-connect to a remote device after the
819 * connection has been dropped. If the device is not in range, the
820 * re-connection will be triggered once the device is back in range.
821 *
822 * @return true, if the connection attempt was initiated successfully
823 */
824 public boolean connect() {
825 try {
826 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700827 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700828 return true;
829 } catch (RemoteException e) {
830 Log.e(TAG,"",e);
831 return false;
832 }
833 }
834
835 /**
836 * Return the remote bluetooth device this GATT client targets to
837 *
838 * @return remote bluetooth device
839 */
840 public BluetoothDevice getDevice() {
841 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800842 }
843
844 /**
845 * Discovers services offered by a remote device as well as their
846 * characteristics and descriptors.
847 *
848 * <p>This is an asynchronous operation. Once service discovery is completed,
849 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
850 * triggered. If the discovery was successful, the remote services can be
851 * retrieved using the {@link #getServices} function.
852 *
853 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
854 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800855 * @return true, if the remote service discovery has been started
856 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800857 public boolean discoverServices() {
858 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800859 if (mService == null || mClientIf == 0) return false;
860
861 mServices.clear();
862
863 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800864 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800865 } catch (RemoteException e) {
866 Log.e(TAG,"",e);
867 return false;
868 }
869
870 return true;
871 }
872
873 /**
874 * Returns a list of GATT services offered by the remote device.
875 *
876 * <p>This function requires that service discovery has been completed
877 * for the given device.
878 *
879 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
880 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800881 * @return List of services on the remote device. Returns an empty list
882 * if service discovery has not yet been performed.
883 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800884 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800885 List<BluetoothGattService> result =
886 new ArrayList<BluetoothGattService>();
887
888 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800889 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800890 result.add(service);
891 }
892 }
893
894 return result;
895 }
896
897 /**
898 * Returns a {@link BluetoothGattService}, if the requested UUID is
899 * supported by the remote device.
900 *
901 * <p>This function requires that service discovery has been completed
902 * for the given device.
903 *
904 * <p>If multiple instances of the same service (as identified by UUID)
905 * exist, the first instance of the service is returned.
906 *
907 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
908 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800909 * @param uuid UUID of the requested service
910 * @return BluetoothGattService if supported, or null if the requested
911 * service is not offered by the remote device.
912 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800913 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800914 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800915 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800916 service.getUuid().equals(uuid)) {
917 return service;
918 }
919 }
920
921 return null;
922 }
923
924 /**
925 * Reads the requested characteristic from the associated remote device.
926 *
927 * <p>This is an asynchronous operation. The result of the read operation
928 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
929 * callback.
930 *
931 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
932 *
933 * @param characteristic Characteristic to read from the remote device
934 * @return true, if the read operation was initiated successfully
935 */
936 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
937 if ((characteristic.getProperties() &
938 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
939
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700940 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800941 if (mService == null || mClientIf == 0) return false;
942
943 BluetoothGattService service = characteristic.getService();
944 if (service == null) return false;
945
946 BluetoothDevice device = service.getDevice();
947 if (device == null) return false;
948
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700949 synchronized(mDeviceBusy) {
950 if (mDeviceBusy) return false;
951 mDeviceBusy = true;
952 }
953
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800954 try {
955 mService.readCharacteristic(mClientIf, device.getAddress(),
956 service.getType(), service.getInstanceId(),
957 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
958 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
959 } catch (RemoteException e) {
960 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700961 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800962 return false;
963 }
964
965 return true;
966 }
967
968 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800969 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800970 *
971 * <p>Once the write operation has been completed, the
972 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
973 * reporting the result of the operation.
974 *
975 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
976 *
977 * @param characteristic Characteristic to write on the remote device
978 * @return true, if the write operation was initiated successfully
979 */
980 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
981 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
982 && (characteristic.getProperties() &
983 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
984
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700985 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800986 if (mService == null || mClientIf == 0) return false;
987
988 BluetoothGattService service = characteristic.getService();
989 if (service == null) return false;
990
991 BluetoothDevice device = service.getDevice();
992 if (device == null) return false;
993
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700994 synchronized(mDeviceBusy) {
995 if (mDeviceBusy) return false;
996 mDeviceBusy = true;
997 }
998
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800999 try {
1000 mService.writeCharacteristic(mClientIf, device.getAddress(),
1001 service.getType(), service.getInstanceId(),
1002 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1003 new ParcelUuid(characteristic.getUuid()),
1004 characteristic.getWriteType(), AUTHENTICATION_NONE,
1005 characteristic.getValue());
1006 } catch (RemoteException e) {
1007 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001008 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001009 return false;
1010 }
1011
1012 return true;
1013 }
1014
1015 /**
1016 * Reads the value for a given descriptor from the associated remote device.
1017 *
1018 * <p>Once the read operation has been completed, the
1019 * {@link BluetoothGattCallback#onDescriptorRead} callback is
1020 * triggered, signaling the result of the operation.
1021 *
1022 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1023 *
1024 * @param descriptor Descriptor value to read from the remote device
1025 * @return true, if the read operation was initiated successfully
1026 */
1027 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001028 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001029 if (mService == null || mClientIf == 0) return false;
1030
1031 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1032 if (characteristic == null) return false;
1033
1034 BluetoothGattService service = characteristic.getService();
1035 if (service == null) return false;
1036
1037 BluetoothDevice device = service.getDevice();
1038 if (device == null) return false;
1039
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001040 synchronized(mDeviceBusy) {
1041 if (mDeviceBusy) return false;
1042 mDeviceBusy = true;
1043 }
1044
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001045 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001046 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
1047 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1048 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1049 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
1050 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001051 } catch (RemoteException e) {
1052 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001053 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001054 return false;
1055 }
1056
1057 return true;
1058 }
1059
1060 /**
1061 * Write the value of a given descriptor to the associated remote device.
1062 *
1063 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
1064 * triggered to report the result of the write operation.
1065 *
1066 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1067 *
1068 * @param descriptor Descriptor to write to the associated remote device
1069 * @return true, if the write operation was initiated successfully
1070 */
1071 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001072 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001073 if (mService == null || mClientIf == 0) return false;
1074
1075 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1076 if (characteristic == null) return false;
1077
1078 BluetoothGattService service = characteristic.getService();
1079 if (service == null) return false;
1080
1081 BluetoothDevice device = service.getDevice();
1082 if (device == null) return false;
1083
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001084 synchronized(mDeviceBusy) {
1085 if (mDeviceBusy) return false;
1086 mDeviceBusy = true;
1087 }
1088
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001089 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -07001090 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
1091 service.getInstanceId(), new ParcelUuid(service.getUuid()),
1092 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
1093 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001094 characteristic.getWriteType(), AUTHENTICATION_NONE,
1095 descriptor.getValue());
1096 } catch (RemoteException e) {
1097 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001098 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001099 return false;
1100 }
1101
1102 return true;
1103 }
1104
1105 /**
1106 * Initiates a reliable write transaction for a given remote device.
1107 *
1108 * <p>Once a reliable write transaction has been initiated, all calls
1109 * to {@link #writeCharacteristic} are sent to the remote device for
1110 * verification and queued up for atomic execution. The application will
1111 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
1112 * in response to every {@link #writeCharacteristic} call and is responsible
1113 * for verifying if the value has been transmitted accurately.
1114 *
1115 * <p>After all characteristics have been queued up and verified,
1116 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1117 * was not written correctly, calling {@link #abortReliableWrite} will
1118 * cancel the current transaction without commiting any values on the
1119 * remote device.
1120 *
1121 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1122 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001123 * @return true, if the reliable write transaction has been initiated
1124 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001125 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001126 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001127 if (mService == null || mClientIf == 0) return false;
1128
1129 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001130 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001131 } catch (RemoteException e) {
1132 Log.e(TAG,"",e);
1133 return false;
1134 }
1135
1136 return true;
1137 }
1138
1139 /**
1140 * Executes a reliable write transaction for a given remote device.
1141 *
1142 * <p>This function will commit all queued up characteristic write
1143 * operations for a given remote device.
1144 *
1145 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1146 * invoked to indicate whether the transaction has been executed correctly.
1147 *
1148 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1149 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001150 * @return true, if the request to execute the transaction has been sent
1151 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001152 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001153 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001154 if (mService == null || mClientIf == 0) return false;
1155
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001156 synchronized(mDeviceBusy) {
1157 if (mDeviceBusy) return false;
1158 mDeviceBusy = true;
1159 }
1160
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001161 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001162 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001163 } catch (RemoteException e) {
1164 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001165 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001166 return false;
1167 }
1168
1169 return true;
1170 }
1171
1172 /**
1173 * Cancels a reliable write transaction for a given device.
1174 *
1175 * <p>Calling this function will discard all queued characteristic write
1176 * operations for a given remote device.
1177 *
1178 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001179 */
John Du48f8b5d2013-08-19 12:20:37 -07001180 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001181 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001182 if (mService == null || mClientIf == 0) return;
1183
1184 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001185 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001186 } catch (RemoteException e) {
1187 Log.e(TAG,"",e);
1188 }
1189 }
1190
1191 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001192 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001193 */
1194 public void abortReliableWrite(BluetoothDevice mDevice) {
1195 abortReliableWrite();
1196 }
1197
1198 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001199 * Enable or disable notifications/indications for a given characteristic.
1200 *
1201 * <p>Once notifications are enabled for a characteristic, a
1202 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1203 * triggered if the remote device indicates that the given characteristic
1204 * has changed.
1205 *
1206 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1207 *
1208 * @param characteristic The characteristic for which to enable notifications
1209 * @param enable Set to true to enable notifications/indications
1210 * @return true, if the requested notification status was set successfully
1211 */
1212 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1213 boolean enable) {
1214 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1215 + " enable: " + enable);
1216 if (mService == null || mClientIf == 0) return false;
1217
1218 BluetoothGattService service = characteristic.getService();
1219 if (service == null) return false;
1220
1221 BluetoothDevice device = service.getDevice();
1222 if (device == null) return false;
1223
1224 try {
1225 mService.registerForNotification(mClientIf, device.getAddress(),
1226 service.getType(), service.getInstanceId(),
1227 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1228 new ParcelUuid(characteristic.getUuid()),
1229 enable);
1230 } catch (RemoteException e) {
1231 Log.e(TAG,"",e);
1232 return false;
1233 }
1234
1235 return true;
1236 }
1237
1238 /**
1239 * Clears the internal cache and forces a refresh of the services from the
1240 * remote device.
1241 * @hide
1242 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001243 public boolean refresh() {
1244 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001245 if (mService == null || mClientIf == 0) return false;
1246
1247 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001248 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001249 } catch (RemoteException e) {
1250 Log.e(TAG,"",e);
1251 return false;
1252 }
1253
1254 return true;
1255 }
1256
1257 /**
1258 * Read the RSSI for a connected remote device.
1259 *
1260 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1261 * invoked when the RSSI value has been read.
1262 *
1263 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1264 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001265 * @return true, if the RSSI value has been requested successfully
1266 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001267 public boolean readRemoteRssi() {
1268 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001269 if (mService == null || mClientIf == 0) return false;
1270
1271 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001272 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001273 } catch (RemoteException e) {
1274 Log.e(TAG,"",e);
1275 return false;
1276 }
1277
1278 return true;
1279 }
1280
1281 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001282 * Configure the MTU used for a given connection.
1283 *
1284 * <p>When performing a write request operation (write without response),
1285 * the data sent is truncated to the MTU size. This function may be used
1286 * to request a larget MTU size to be able to send more data at once.
1287 *
1288 * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate
1289 * whether this operation was successful.
1290 *
1291 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1292 *
1293 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001294 */
1295 public boolean configureMTU(int mtu) {
1296 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1297 + " mtu: " + mtu);
1298 if (mService == null || mClientIf == 0) return false;
1299
1300 try {
1301 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1302 } catch (RemoteException e) {
1303 Log.e(TAG,"",e);
1304 return false;
1305 }
1306
1307 return true;
1308 }
1309
1310 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001311 * Request a connection parameter update.
1312 *
1313 * <p>This function will send a connection parameter update request to the
1314 * remote device.
1315 *
1316 * @param connectionPriority Request a specific connection priority. Must be one of
1317 * {@link BluetoothGatt#GATT_CONNECTION_BALANCED},
1318 * {@link BluetoothGatt#GATT_CONNECTION_HIGH_PRIORITY}
1319 * or {@link BluetoothGatt#GATT_CONNECTION_LOW_POWER}.
1320 * @throws IllegalArgumentException If the parameters are outside of their
1321 * specified range.
1322 */
1323 public boolean requestConnectionParameterUpdate(int connectionPriority) {
1324 if (connectionPriority < GATT_CONNECTION_BALANCED ||
1325 connectionPriority > GATT_CONNECTION_LOW_POWER) {
1326 throw new IllegalArgumentException("connectionPriority not within valid range");
1327 }
1328
1329 if (DBG) Log.d(TAG, "requestConnectionParameterUpdate() - params: " + connectionPriority);
1330 if (mService == null || mClientIf == 0) return false;
1331
1332 try {
1333 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1334 } catch (RemoteException e) {
1335 Log.e(TAG,"",e);
1336 return false;
1337 }
1338
1339 return true;
1340 }
1341
1342 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001343 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1344 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001345 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001346 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001347 */
1348 @Override
1349 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001350 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001351 }
1352
1353 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001354 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1355 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001356 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001357 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001358 */
1359 @Override
1360 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001361 throw new UnsupportedOperationException
1362 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001363 }
1364
1365 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001366 * Not supported - please use
1367 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1368 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001369 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001370 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001371 */
1372 @Override
1373 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001374 throw new UnsupportedOperationException
1375 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001376 }
1377}