blob: ef056654d7efcb9d5e311baaf008d35cfd0b38c7 [file] [log] [blame]
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080019import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080020import android.os.ParcelUuid;
21import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.util.Log;
23
24import java.util.ArrayList;
25import java.util.List;
26import java.util.UUID;
27
28/**
Matthew Xieddf7e472013-03-01 18:41:02 -080029 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080030 *
Matthew Xieddf7e472013-03-01 18:41:02 -080031 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080032 * with Bluetooth Smart or Smart Ready devices.
33 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080034 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070035 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080036 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
37 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080038 */
39public final class BluetoothGatt implements BluetoothProfile {
40 private static final String TAG = "BluetoothGatt";
41 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070042 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080043
Matthew Xieddf7e472013-03-01 18:41:02 -080044 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045 private IBluetoothGatt mService;
46 private BluetoothGattCallback mCallback;
47 private int mClientIf;
48 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080049 private BluetoothDevice mDevice;
50 private boolean mAutoConnect;
51 private int mConnState;
52 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070053 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070054 private int mTransport;
Matthew Xieddf7e472013-03-01 18:41:02 -080055
56 private static final int CONN_STATE_IDLE = 0;
57 private static final int CONN_STATE_CONNECTING = 1;
58 private static final int CONN_STATE_CONNECTED = 2;
59 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070060 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080061
62 private List<BluetoothGattService> mServices;
63
Matthew Xieddf7e472013-03-01 18:41:02 -080064 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065 public static final int GATT_SUCCESS = 0;
66
Matthew Xieddf7e472013-03-01 18:41:02 -080067 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080068 public static final int GATT_READ_NOT_PERMITTED = 0x2;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
72
73 /** Insufficient authentication for a given operation */
74 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
75
76 /** The given request is not supported */
77 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
78
79 /** Insufficient encryption for a given operation */
80 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
81
82 /** A read or write operation was requested with an invalid offset */
83 public static final int GATT_INVALID_OFFSET = 0x7;
84
85 /** A write operation exceeds the maximum length of the attribute */
86 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
87
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070088 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070089 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
90
Matthew Xie90ca8072013-05-28 21:06:50 +000091 /** A GATT operation failed, errors other than the above */
92 public static final int GATT_FAILURE = 0x101;
93
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080094 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070095 * Connection paramter update - Use the connection paramters recommended by the
96 * Bluetooth SIG. This is the default value if no connection parameter update
97 * is requested.
98 */
Andre Eisenbach4072da02014-08-19 17:58:55 -070099 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700100
101 /**
102 * Connection paramter update - Request a high priority, low latency connection.
103 * An application should only request high priority connection paramters to transfer
104 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700105 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700106 * to reduce energy use.
107 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700108 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700109
110 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700111 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112
113 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800114 * No authentication required.
115 * @hide
116 */
117 /*package*/ static final int AUTHENTICATION_NONE = 0;
118
119 /**
120 * Authentication requested; no man-in-the-middle protection required.
121 * @hide
122 */
123 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
124
125 /**
126 * Authentication with man-in-the-middle protection requested.
127 * @hide
128 */
129 /*package*/ static final int AUTHENTICATION_MITM = 2;
130
131 /**
Wei Wange0d4afb2014-07-29 21:34:25 -0700132 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800133 */
134 private final IBluetoothGattCallback mBluetoothGattCallback =
Wei Wange0d4afb2014-07-29 21:34:25 -0700135 new BluetoothGattCallbackWrapper() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800136 /**
137 * Application interface registered - app is ready to go
138 * @hide
139 */
140 public void onClientRegistered(int status, int clientIf) {
141 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
142 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800143 if (VDBG) {
144 synchronized(mStateLock) {
145 if (mConnState != CONN_STATE_CONNECTING) {
146 Log.e(TAG, "Bad connection state: " + mConnState);
147 }
148 }
149 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800150 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800151 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700152 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800153 BluetoothProfile.STATE_DISCONNECTED);
154 synchronized(mStateLock) {
155 mConnState = CONN_STATE_IDLE;
156 }
157 return;
158 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800159 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800160 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700161 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800162 } catch (RemoteException e) {
163 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800164 }
165 }
166
167 /**
168 * Client connection state changed
169 * @hide
170 */
171 public void onClientConnectionState(int status, int clientIf,
172 boolean connected, String address) {
173 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
174 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800175 if (!address.equals(mDevice.getAddress())) {
176 return;
177 }
178 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
179 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800180 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700181 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800182 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700183 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800184 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800185
186 synchronized(mStateLock) {
187 if (connected) {
188 mConnState = CONN_STATE_CONNECTED;
189 } else {
190 mConnState = CONN_STATE_IDLE;
191 }
192 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700193
194 synchronized(mDeviceBusy) {
195 mDeviceBusy = false;
196 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800197 }
198
199 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800200 * Remote search has been completed.
201 * The internal object structure should now reflect the state
202 * of the remote device database. Let the application know that
203 * we are done at this point.
204 * @hide
205 */
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800206 public void onSearchComplete(String address, List<BluetoothGattService> services,
207 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800208 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800209 if (!address.equals(mDevice.getAddress())) {
210 return;
211 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800212
213 for (BluetoothGattService s : services) {
214 //services we receive don't have device set properly.
215 s.setDevice(mDevice);
216 }
217
218 mServices.addAll(services);
219
220 // Fix references to included services, as they doesn't point to right objects.
221 for (BluetoothGattService fixedService : mServices) {
222 ArrayList<BluetoothGattService> includedServices =
223 new ArrayList(fixedService.getIncludedServices());
224 fixedService.getIncludedServices().clear();
225
226 for(BluetoothGattService brokenRef : includedServices) {
227 BluetoothGattService includedService = getService(mDevice,
228 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
229 if (includedService != null) {
230 fixedService.addIncludedService(includedService);
231 } else {
232 Log.e(TAG, "Broken GATT database: can't find included service.");
233 }
234 }
235 }
236
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800237 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700238 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800239 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700240 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800241 }
242 }
243
244 /**
245 * Remote characteristic has been read.
246 * Updates the internal value.
247 * @hide
248 */
249 public void onCharacteristicRead(String address, int status, int srvcType,
250 int srvcInstId, ParcelUuid srvcUuid,
251 int charInstId, ParcelUuid charUuid, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700252 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800253 + " UUID=" + charUuid + " Status=" + status);
254
Matthew Xieddf7e472013-03-01 18:41:02 -0800255 if (!address.equals(mDevice.getAddress())) {
256 return;
257 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700258
259 synchronized(mDeviceBusy) {
260 mDeviceBusy = false;
261 }
262
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800263 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
264 || status == GATT_INSUFFICIENT_ENCRYPTION)
265 && mAuthRetry == false) {
266 try {
267 mAuthRetry = true;
268 mService.readCharacteristic(mClientIf, address,
269 srvcType, srvcInstId, srvcUuid,
270 charInstId, charUuid, AUTHENTICATION_MITM);
271 return;
272 } catch (RemoteException e) {
273 Log.e(TAG,"",e);
274 }
275 }
276
277 mAuthRetry = false;
278
Matthew Xieddf7e472013-03-01 18:41:02 -0800279 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800280 srvcInstId, srvcType);
281 if (service == null) return;
282
283 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
284 charUuid.getUuid(), charInstId);
285 if (characteristic == null) return;
286
287 if (status == 0) characteristic.setValue(value);
288
289 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700290 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800291 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700292 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800293 }
294 }
295
296 /**
297 * Characteristic has been written to the remote device.
298 * Let the app know how we did...
299 * @hide
300 */
301 public void onCharacteristicWrite(String address, int status, int srvcType,
302 int srvcInstId, ParcelUuid srvcUuid,
303 int charInstId, ParcelUuid charUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700304 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800305 + " UUID=" + charUuid + " Status=" + status);
306
Matthew Xieddf7e472013-03-01 18:41:02 -0800307 if (!address.equals(mDevice.getAddress())) {
308 return;
309 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700310
311 synchronized(mDeviceBusy) {
312 mDeviceBusy = false;
313 }
314
Matthew Xieddf7e472013-03-01 18:41:02 -0800315 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 srvcInstId, srvcType);
317 if (service == null) return;
318
319 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
320 charUuid.getUuid(), charInstId);
321 if (characteristic == null) return;
322
323 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
324 || status == GATT_INSUFFICIENT_ENCRYPTION)
325 && mAuthRetry == false) {
326 try {
327 mAuthRetry = true;
328 mService.writeCharacteristic(mClientIf, address,
329 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
330 characteristic.getWriteType(), AUTHENTICATION_MITM,
331 characteristic.getValue());
332 return;
333 } catch (RemoteException e) {
334 Log.e(TAG,"",e);
335 }
336 }
337
338 mAuthRetry = false;
339
340 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700341 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800342 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700343 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800344 }
345 }
346
347 /**
348 * Remote characteristic has been updated.
349 * Updates the internal value.
350 * @hide
351 */
352 public void onNotify(String address, int srvcType,
353 int srvcInstId, ParcelUuid srvcUuid,
354 int charInstId, ParcelUuid charUuid,
355 byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700356 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357
Matthew Xieddf7e472013-03-01 18:41:02 -0800358 if (!address.equals(mDevice.getAddress())) {
359 return;
360 }
361 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800362 srvcInstId, srvcType);
363 if (service == null) return;
364
365 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
366 charUuid.getUuid(), charInstId);
367 if (characteristic == null) return;
368
369 characteristic.setValue(value);
370
371 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700372 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800373 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700374 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800375 }
376 }
377
378 /**
379 * Descriptor has been read.
380 * @hide
381 */
382 public void onDescriptorRead(String address, int status, int srvcType,
383 int srvcInstId, ParcelUuid srvcUuid,
384 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700385 int descrInstId, ParcelUuid descrUuid,
386 byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700387 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800388
Matthew Xieddf7e472013-03-01 18:41:02 -0800389 if (!address.equals(mDevice.getAddress())) {
390 return;
391 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700392
393 synchronized(mDeviceBusy) {
394 mDeviceBusy = false;
395 }
396
Matthew Xieddf7e472013-03-01 18:41:02 -0800397 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800398 srvcInstId, srvcType);
399 if (service == null) return;
400
401 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
402 charUuid.getUuid(), charInstId);
403 if (characteristic == null) return;
404
405 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700406 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800407 if (descriptor == null) return;
408
409 if (status == 0) descriptor.setValue(value);
410
411 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
412 || status == GATT_INSUFFICIENT_ENCRYPTION)
413 && mAuthRetry == false) {
414 try {
415 mAuthRetry = true;
416 mService.readDescriptor(mClientIf, address,
417 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700418 descrInstId, descrUuid, AUTHENTICATION_MITM);
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700419 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800420 } catch (RemoteException e) {
421 Log.e(TAG,"",e);
422 }
423 }
424
425 mAuthRetry = true;
426
427 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700428 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800429 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700430 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800431 }
432 }
433
434 /**
435 * Descriptor write operation complete.
436 * @hide
437 */
438 public void onDescriptorWrite(String address, int status, int srvcType,
439 int srvcInstId, ParcelUuid srvcUuid,
440 int charInstId, ParcelUuid charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700441 int descrInstId, ParcelUuid descrUuid) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700442 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800443
Matthew Xieddf7e472013-03-01 18:41:02 -0800444 if (!address.equals(mDevice.getAddress())) {
445 return;
446 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700447
448 synchronized(mDeviceBusy) {
449 mDeviceBusy = false;
450 }
451
Matthew Xieddf7e472013-03-01 18:41:02 -0800452 BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800453 srvcInstId, srvcType);
454 if (service == null) return;
455
456 BluetoothGattCharacteristic characteristic = service.getCharacteristic(
457 charUuid.getUuid(), charInstId);
458 if (characteristic == null) return;
459
460 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700461 descrUuid.getUuid(), descrInstId);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800462 if (descriptor == null) return;
463
464 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
465 || status == GATT_INSUFFICIENT_ENCRYPTION)
466 && mAuthRetry == false) {
467 try {
468 mAuthRetry = true;
469 mService.writeDescriptor(mClientIf, address,
470 srvcType, srvcInstId, srvcUuid, charInstId, charUuid,
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700471 descrInstId, descrUuid, characteristic.getWriteType(),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800472 AUTHENTICATION_MITM, descriptor.getValue());
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700473 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800474 } catch (RemoteException e) {
475 Log.e(TAG,"",e);
476 }
477 }
478
479 mAuthRetry = false;
480
481 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700482 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800483 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700484 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800485 }
486 }
487
488 /**
489 * Prepared write transaction completed (or aborted)
490 * @hide
491 */
492 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700493 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800494 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800495 if (!address.equals(mDevice.getAddress())) {
496 return;
497 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700498
499 synchronized(mDeviceBusy) {
500 mDeviceBusy = false;
501 }
502
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800503 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700504 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800505 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700506 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800507 }
508 }
509
510 /**
511 * Remote device RSSI has been read
512 * @hide
513 */
514 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700515 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800516 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800517 if (!address.equals(mDevice.getAddress())) {
518 return;
519 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800520 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700521 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800522 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700523 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800524 }
525 }
Wei Wangf3055892014-03-11 22:22:41 -0700526
527 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700528 * Callback invoked when the MTU for a given connection changes
529 * @hide
530 */
531 public void onConfigureMTU(String address, int mtu, int status) {
532 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
533 " mtu=" + mtu + " status=" + status);
534 if (!address.equals(mDevice.getAddress())) {
535 return;
536 }
537 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700538 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700539 } catch (Exception ex) {
540 Log.w(TAG, "Unhandled exception in callback", ex);
541 }
Wei Wangf3055892014-03-11 22:22:41 -0700542 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800543 };
544
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700545 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
546 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800547 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800548 mService = iGatt;
549 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700550 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800551 mServices = new ArrayList<BluetoothGattService>();
552
Matthew Xieddf7e472013-03-01 18:41:02 -0800553 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800554 }
555
556 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700557 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700558 *
559 * Application should call this method as early as possible after it is done with
560 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800561 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700562 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800563 if (DBG) Log.d(TAG, "close()");
564
565 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700566 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800567 }
568
569 /**
570 * Returns a service by UUID, instance and type.
571 * @hide
572 */
573 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
574 int instanceId, int type) {
575 for(BluetoothGattService svc : mServices) {
576 if (svc.getDevice().equals(device) &&
577 svc.getType() == type &&
578 svc.getInstanceId() == instanceId &&
579 svc.getUuid().equals(uuid)) {
580 return svc;
581 }
582 }
583 return null;
584 }
585
586
587 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800588 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800589 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800590 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
591 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800592 *
593 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
594 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800595 * @param callback GATT callback handler that will receive asynchronous callbacks.
596 * @return If true, the callback will be called to notify success or failure,
597 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800598 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800599 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800600 if (DBG) Log.d(TAG, "registerApp()");
601 if (mService == null) return false;
602
603 mCallback = callback;
604 UUID uuid = UUID.randomUUID();
605 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
606
607 try {
608 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
609 } catch (RemoteException e) {
610 Log.e(TAG,"",e);
611 return false;
612 }
613
614 return true;
615 }
616
617 /**
618 * Unregister the current application and callbacks.
619 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800620 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800621 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
622 if (mService == null || mClientIf == 0) return;
623
624 try {
625 mCallback = null;
626 mService.unregisterClient(mClientIf);
627 mClientIf = 0;
628 } catch (RemoteException e) {
629 Log.e(TAG,"",e);
630 }
631 }
632
633 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800634 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800635 *
636 * <p>The connection may not be established right away, but will be
637 * completed when the remote device is available. A
638 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
639 * invoked when the connection state changes as a result of this function.
640 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700641 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800642 * the remote device, or rather passively scan and finalize the connection
643 * when the remote device is in range/available. Generally, the first ever
644 * connection to a device should be direct (autoConnect set to false) and
645 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800646 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800647 *
648 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
649 *
650 * @param device Remote device to connect to
651 * @param autoConnect Whether to directly connect to the remote device (false)
652 * or to automatically connect as soon as the remote
653 * device becomes available (true).
654 * @return true, if the connection attempt was initiated successfully
655 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800656 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
657 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
658 synchronized(mStateLock) {
659 if (mConnState != CONN_STATE_IDLE) {
660 throw new IllegalStateException("Not idle");
661 }
662 mConnState = CONN_STATE_CONNECTING;
663 }
664 if (!registerApp(callback)) {
665 synchronized(mStateLock) {
666 mConnState = CONN_STATE_IDLE;
667 }
668 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800669 return false;
670 }
671
Matthew Xieddf7e472013-03-01 18:41:02 -0800672 // the connection will continue after successful callback registration
673 mAutoConnect = autoConnect;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800674 return true;
675 }
676
677 /**
678 * Disconnects an established connection, or cancels a connection attempt
679 * currently in progress.
680 *
681 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800683 public void disconnect() {
684 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800685 if (mService == null || mClientIf == 0) return;
686
687 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800688 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800689 } catch (RemoteException e) {
690 Log.e(TAG,"",e);
691 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700692 }
693
694 /**
695 * Connect back to remote device.
696 *
697 * <p>This method is used to re-connect to a remote device after the
698 * connection has been dropped. If the device is not in range, the
699 * re-connection will be triggered once the device is back in range.
700 *
701 * @return true, if the connection attempt was initiated successfully
702 */
703 public boolean connect() {
704 try {
705 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700706 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700707 return true;
708 } catch (RemoteException e) {
709 Log.e(TAG,"",e);
710 return false;
711 }
712 }
713
714 /**
715 * Return the remote bluetooth device this GATT client targets to
716 *
717 * @return remote bluetooth device
718 */
719 public BluetoothDevice getDevice() {
720 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 }
722
723 /**
724 * Discovers services offered by a remote device as well as their
725 * characteristics and descriptors.
726 *
727 * <p>This is an asynchronous operation. Once service discovery is completed,
728 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
729 * triggered. If the discovery was successful, the remote services can be
730 * retrieved using the {@link #getServices} function.
731 *
732 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
733 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800734 * @return true, if the remote service discovery has been started
735 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800736 public boolean discoverServices() {
737 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800738 if (mService == null || mClientIf == 0) return false;
739
740 mServices.clear();
741
742 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800743 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800744 } catch (RemoteException e) {
745 Log.e(TAG,"",e);
746 return false;
747 }
748
749 return true;
750 }
751
752 /**
753 * Returns a list of GATT services offered by the remote device.
754 *
755 * <p>This function requires that service discovery has been completed
756 * for the given device.
757 *
758 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
759 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800760 * @return List of services on the remote device. Returns an empty list
761 * if service discovery has not yet been performed.
762 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800763 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800764 List<BluetoothGattService> result =
765 new ArrayList<BluetoothGattService>();
766
767 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800768 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800769 result.add(service);
770 }
771 }
772
773 return result;
774 }
775
776 /**
777 * Returns a {@link BluetoothGattService}, if the requested UUID is
778 * supported by the remote device.
779 *
780 * <p>This function requires that service discovery has been completed
781 * for the given device.
782 *
783 * <p>If multiple instances of the same service (as identified by UUID)
784 * exist, the first instance of the service is returned.
785 *
786 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
787 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800788 * @param uuid UUID of the requested service
789 * @return BluetoothGattService if supported, or null if the requested
790 * service is not offered by the remote device.
791 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800792 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800793 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800794 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800795 service.getUuid().equals(uuid)) {
796 return service;
797 }
798 }
799
800 return null;
801 }
802
803 /**
804 * Reads the requested characteristic from the associated remote device.
805 *
806 * <p>This is an asynchronous operation. The result of the read operation
807 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
808 * callback.
809 *
810 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
811 *
812 * @param characteristic Characteristic to read from the remote device
813 * @return true, if the read operation was initiated successfully
814 */
815 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
816 if ((characteristic.getProperties() &
817 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
818
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700819 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800820 if (mService == null || mClientIf == 0) return false;
821
822 BluetoothGattService service = characteristic.getService();
823 if (service == null) return false;
824
825 BluetoothDevice device = service.getDevice();
826 if (device == null) return false;
827
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700828 synchronized(mDeviceBusy) {
829 if (mDeviceBusy) return false;
830 mDeviceBusy = true;
831 }
832
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800833 try {
834 mService.readCharacteristic(mClientIf, device.getAddress(),
835 service.getType(), service.getInstanceId(),
836 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
837 new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE);
838 } catch (RemoteException e) {
839 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700840 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800841 return false;
842 }
843
844 return true;
845 }
846
847 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800848 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800849 *
850 * <p>Once the write operation has been completed, the
851 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
852 * reporting the result of the operation.
853 *
854 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
855 *
856 * @param characteristic Characteristic to write on the remote device
857 * @return true, if the write operation was initiated successfully
858 */
859 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
860 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
861 && (characteristic.getProperties() &
862 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
863
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700864 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800865 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800866
867 BluetoothGattService service = characteristic.getService();
868 if (service == null) return false;
869
870 BluetoothDevice device = service.getDevice();
871 if (device == null) return false;
872
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700873 synchronized(mDeviceBusy) {
874 if (mDeviceBusy) return false;
875 mDeviceBusy = true;
876 }
877
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800878 try {
879 mService.writeCharacteristic(mClientIf, device.getAddress(),
880 service.getType(), service.getInstanceId(),
881 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
882 new ParcelUuid(characteristic.getUuid()),
883 characteristic.getWriteType(), AUTHENTICATION_NONE,
884 characteristic.getValue());
885 } catch (RemoteException e) {
886 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700887 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800888 return false;
889 }
890
891 return true;
892 }
893
894 /**
895 * Reads the value for a given descriptor from the associated remote device.
896 *
897 * <p>Once the read operation has been completed, the
898 * {@link BluetoothGattCallback#onDescriptorRead} callback is
899 * triggered, signaling the result of the operation.
900 *
901 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
902 *
903 * @param descriptor Descriptor value to read from the remote device
904 * @return true, if the read operation was initiated successfully
905 */
906 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700907 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800908 if (mService == null || mClientIf == 0) return false;
909
910 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
911 if (characteristic == null) return false;
912
913 BluetoothGattService service = characteristic.getService();
914 if (service == null) return false;
915
916 BluetoothDevice device = service.getDevice();
917 if (device == null) return false;
918
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700919 synchronized(mDeviceBusy) {
920 if (mDeviceBusy) return false;
921 mDeviceBusy = true;
922 }
923
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800924 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700925 mService.readDescriptor(mClientIf, device.getAddress(), service.getType(),
926 service.getInstanceId(), new ParcelUuid(service.getUuid()),
927 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
928 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
929 AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800930 } catch (RemoteException e) {
931 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700932 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800933 return false;
934 }
935
936 return true;
937 }
938
939 /**
940 * Write the value of a given descriptor to the associated remote device.
941 *
942 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
943 * triggered to report the result of the write operation.
944 *
945 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
946 *
947 * @param descriptor Descriptor to write to the associated remote device
948 * @return true, if the write operation was initiated successfully
949 */
950 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700951 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800952 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800953
954 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
955 if (characteristic == null) return false;
956
957 BluetoothGattService service = characteristic.getService();
958 if (service == null) return false;
959
960 BluetoothDevice device = service.getDevice();
961 if (device == null) return false;
962
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700963 synchronized(mDeviceBusy) {
964 if (mDeviceBusy) return false;
965 mDeviceBusy = true;
966 }
967
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800968 try {
Andre Eisenbach25b9cf92013-07-08 23:58:16 -0700969 mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(),
970 service.getInstanceId(), new ParcelUuid(service.getUuid()),
971 characteristic.getInstanceId(), new ParcelUuid(characteristic.getUuid()),
972 descriptor.getInstanceId(), new ParcelUuid(descriptor.getUuid()),
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800973 characteristic.getWriteType(), AUTHENTICATION_NONE,
974 descriptor.getValue());
975 } catch (RemoteException e) {
976 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700977 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800978 return false;
979 }
980
981 return true;
982 }
983
984 /**
985 * Initiates a reliable write transaction for a given remote device.
986 *
987 * <p>Once a reliable write transaction has been initiated, all calls
988 * to {@link #writeCharacteristic} are sent to the remote device for
989 * verification and queued up for atomic execution. The application will
990 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
991 * in response to every {@link #writeCharacteristic} call and is responsible
992 * for verifying if the value has been transmitted accurately.
993 *
994 * <p>After all characteristics have been queued up and verified,
995 * {@link #executeReliableWrite} will execute all writes. If a characteristic
996 * was not written correctly, calling {@link #abortReliableWrite} will
997 * cancel the current transaction without commiting any values on the
998 * remote device.
999 *
1000 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1001 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001002 * @return true, if the reliable write transaction has been initiated
1003 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001004 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001005 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001006 if (mService == null || mClientIf == 0) return false;
1007
1008 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001009 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001010 } catch (RemoteException e) {
1011 Log.e(TAG,"",e);
1012 return false;
1013 }
1014
1015 return true;
1016 }
1017
1018 /**
1019 * Executes a reliable write transaction for a given remote device.
1020 *
1021 * <p>This function will commit all queued up characteristic write
1022 * operations for a given remote device.
1023 *
1024 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1025 * invoked to indicate whether the transaction has been executed correctly.
1026 *
1027 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1028 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001029 * @return true, if the request to execute the transaction has been sent
1030 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001031 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001032 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001033 if (mService == null || mClientIf == 0) return false;
1034
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001035 synchronized(mDeviceBusy) {
1036 if (mDeviceBusy) return false;
1037 mDeviceBusy = true;
1038 }
1039
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001040 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001041 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001042 } catch (RemoteException e) {
1043 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001044 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001045 return false;
1046 }
1047
1048 return true;
1049 }
1050
1051 /**
1052 * Cancels a reliable write transaction for a given device.
1053 *
1054 * <p>Calling this function will discard all queued characteristic write
1055 * operations for a given remote device.
1056 *
1057 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001058 */
John Du48f8b5d2013-08-19 12:20:37 -07001059 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001060 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001061 if (mService == null || mClientIf == 0) return;
1062
1063 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001064 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001065 } catch (RemoteException e) {
1066 Log.e(TAG,"",e);
1067 }
1068 }
1069
1070 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001071 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001072 */
1073 public void abortReliableWrite(BluetoothDevice mDevice) {
1074 abortReliableWrite();
1075 }
1076
1077 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001078 * Enable or disable notifications/indications for a given characteristic.
1079 *
1080 * <p>Once notifications are enabled for a characteristic, a
1081 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1082 * triggered if the remote device indicates that the given characteristic
1083 * has changed.
1084 *
1085 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1086 *
1087 * @param characteristic The characteristic for which to enable notifications
1088 * @param enable Set to true to enable notifications/indications
1089 * @return true, if the requested notification status was set successfully
1090 */
1091 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1092 boolean enable) {
1093 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1094 + " enable: " + enable);
1095 if (mService == null || mClientIf == 0) return false;
1096
1097 BluetoothGattService service = characteristic.getService();
1098 if (service == null) return false;
1099
1100 BluetoothDevice device = service.getDevice();
1101 if (device == null) return false;
1102
1103 try {
1104 mService.registerForNotification(mClientIf, device.getAddress(),
1105 service.getType(), service.getInstanceId(),
1106 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
1107 new ParcelUuid(characteristic.getUuid()),
1108 enable);
1109 } catch (RemoteException e) {
1110 Log.e(TAG,"",e);
1111 return false;
1112 }
1113
1114 return true;
1115 }
1116
1117 /**
1118 * Clears the internal cache and forces a refresh of the services from the
1119 * remote device.
1120 * @hide
1121 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001122 public boolean refresh() {
1123 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 if (mService == null || mClientIf == 0) return false;
1125
1126 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001127 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001128 } catch (RemoteException e) {
1129 Log.e(TAG,"",e);
1130 return false;
1131 }
1132
1133 return true;
1134 }
1135
1136 /**
1137 * Read the RSSI for a connected remote device.
1138 *
1139 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1140 * invoked when the RSSI value has been read.
1141 *
1142 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1143 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001144 * @return true, if the RSSI value has been requested successfully
1145 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001146 public boolean readRemoteRssi() {
1147 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001148 if (mService == null || mClientIf == 0) return false;
1149
1150 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001151 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001152 } catch (RemoteException e) {
1153 Log.e(TAG,"",e);
1154 return false;
1155 }
1156
1157 return true;
1158 }
1159
1160 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001161 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001162 *
1163 * <p>When performing a write request operation (write without response),
1164 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001165 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001166 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001167 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001168 * whether this operation was successful.
1169 *
1170 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1171 *
1172 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001173 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001174 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001175 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1176 + " mtu: " + mtu);
1177 if (mService == null || mClientIf == 0) return false;
1178
1179 try {
1180 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1181 } catch (RemoteException e) {
1182 Log.e(TAG,"",e);
1183 return false;
1184 }
1185
1186 return true;
1187 }
1188
1189 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001190 * Request a connection parameter update.
1191 *
1192 * <p>This function will send a connection parameter update request to the
1193 * remote device.
1194 *
1195 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001196 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1197 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1198 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001199 * @throws IllegalArgumentException If the parameters are outside of their
1200 * specified range.
1201 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001202 public boolean requestConnectionPriority(int connectionPriority) {
1203 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1204 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001205 throw new IllegalArgumentException("connectionPriority not within valid range");
1206 }
1207
Andre Eisenbach4072da02014-08-19 17:58:55 -07001208 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001209 if (mService == null || mClientIf == 0) return false;
1210
1211 try {
1212 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1213 } catch (RemoteException e) {
1214 Log.e(TAG,"",e);
1215 return false;
1216 }
1217
1218 return true;
1219 }
1220
1221 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001222 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1223 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001224 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001225 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001226 */
1227 @Override
1228 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001229 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001230 }
1231
1232 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001233 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1234 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001235 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001236 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001237 */
1238 @Override
1239 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001240 throw new UnsupportedOperationException
1241 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001242 }
1243
1244 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001245 * Not supported - please use
1246 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1247 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001248 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001249 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001250 */
1251 @Override
1252 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001253 throw new UnsupportedOperationException
1254 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001255 }
1256}