blob: 5419143ff4240f41ec2e999712cf3afc75ac68d4 [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 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700249 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700250 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700251 + " handle=" + handle + " Status=" + status);
252
253 Log.w(TAG, "onCharacteristicRead() - Device=" + address
254 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800255
Matthew Xieddf7e472013-03-01 18:41:02 -0800256 if (!address.equals(mDevice.getAddress())) {
257 return;
258 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700259
260 synchronized(mDeviceBusy) {
261 mDeviceBusy = false;
262 }
263
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800264 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
265 || status == GATT_INSUFFICIENT_ENCRYPTION)
266 && mAuthRetry == false) {
267 try {
268 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700269 mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800270 return;
271 } catch (RemoteException e) {
272 Log.e(TAG,"",e);
273 }
274 }
275
276 mAuthRetry = false;
277
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700278 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
279 if (characteristic == null) {
280 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
281 return;
282 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800283
284 if (status == 0) characteristic.setValue(value);
285
286 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700287 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800288 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700289 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800290 }
291 }
292
293 /**
294 * Characteristic has been written to the remote device.
295 * Let the app know how we did...
296 * @hide
297 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700298 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700299 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700300 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800301
Matthew Xieddf7e472013-03-01 18:41:02 -0800302 if (!address.equals(mDevice.getAddress())) {
303 return;
304 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700305
306 synchronized(mDeviceBusy) {
307 mDeviceBusy = false;
308 }
309
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700310 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800311 if (characteristic == null) return;
312
313 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
314 || status == GATT_INSUFFICIENT_ENCRYPTION)
315 && mAuthRetry == false) {
316 try {
317 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700318 mService.writeCharacteristic(mClientIf, address, handle,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800319 characteristic.getWriteType(), AUTHENTICATION_MITM,
320 characteristic.getValue());
321 return;
322 } catch (RemoteException e) {
323 Log.e(TAG,"",e);
324 }
325 }
326
327 mAuthRetry = false;
328
329 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700330 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800331 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700332 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800333 }
334 }
335
336 /**
337 * Remote characteristic has been updated.
338 * Updates the internal value.
339 * @hide
340 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700341 public void onNotify(String address, int handle, byte[] value) {
342 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800343
Matthew Xieddf7e472013-03-01 18:41:02 -0800344 if (!address.equals(mDevice.getAddress())) {
345 return;
346 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800347
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700348 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800349 if (characteristic == null) return;
350
351 characteristic.setValue(value);
352
353 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700354 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800355 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700356 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357 }
358 }
359
360 /**
361 * Descriptor has been read.
362 * @hide
363 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700364 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
365 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800366
Matthew Xieddf7e472013-03-01 18:41:02 -0800367 if (!address.equals(mDevice.getAddress())) {
368 return;
369 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700370
371 synchronized(mDeviceBusy) {
372 mDeviceBusy = false;
373 }
374
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700375 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800376 if (descriptor == null) return;
377
378 if (status == 0) descriptor.setValue(value);
379
380 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
381 || status == GATT_INSUFFICIENT_ENCRYPTION)
382 && mAuthRetry == false) {
383 try {
384 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700385 mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM);
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700386 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800387 } catch (RemoteException e) {
388 Log.e(TAG,"",e);
389 }
390 }
391
392 mAuthRetry = true;
393
394 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700395 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800396 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700397 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800398 }
399 }
400
401 /**
402 * Descriptor write operation complete.
403 * @hide
404 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700405 public void onDescriptorWrite(String address, int status, int handle) {
406 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800407
Matthew Xieddf7e472013-03-01 18:41:02 -0800408 if (!address.equals(mDevice.getAddress())) {
409 return;
410 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700411
412 synchronized(mDeviceBusy) {
413 mDeviceBusy = false;
414 }
415
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700416 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800417 if (descriptor == null) return;
418
419 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
420 || status == GATT_INSUFFICIENT_ENCRYPTION)
421 && mAuthRetry == false) {
422 try {
423 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700424 mService.writeDescriptor(mClientIf, address, handle,
Andre Eisenbach613f4302016-03-29 20:52:38 -0700425 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800426 AUTHENTICATION_MITM, descriptor.getValue());
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700427 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800428 } catch (RemoteException e) {
429 Log.e(TAG,"",e);
430 }
431 }
432
433 mAuthRetry = false;
434
435 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700436 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800437 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700438 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800439 }
440 }
441
442 /**
443 * Prepared write transaction completed (or aborted)
444 * @hide
445 */
446 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700447 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800448 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800449 if (!address.equals(mDevice.getAddress())) {
450 return;
451 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700452
453 synchronized(mDeviceBusy) {
454 mDeviceBusy = false;
455 }
456
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800457 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700458 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800459 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700460 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800461 }
462 }
463
464 /**
465 * Remote device RSSI has been read
466 * @hide
467 */
468 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700469 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800470 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800471 if (!address.equals(mDevice.getAddress())) {
472 return;
473 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800474 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700475 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800476 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700477 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800478 }
479 }
Wei Wangf3055892014-03-11 22:22:41 -0700480
481 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700482 * Callback invoked when the MTU for a given connection changes
483 * @hide
484 */
485 public void onConfigureMTU(String address, int mtu, int status) {
486 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
487 " mtu=" + mtu + " status=" + status);
488 if (!address.equals(mDevice.getAddress())) {
489 return;
490 }
491 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700492 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700493 } catch (Exception ex) {
494 Log.w(TAG, "Unhandled exception in callback", ex);
495 }
Wei Wangf3055892014-03-11 22:22:41 -0700496 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800497 };
498
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700499 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
500 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800501 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800502 mService = iGatt;
503 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700504 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800505 mServices = new ArrayList<BluetoothGattService>();
506
Matthew Xieddf7e472013-03-01 18:41:02 -0800507 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800508 }
509
510 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700511 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700512 *
513 * Application should call this method as early as possible after it is done with
514 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800515 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700516 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800517 if (DBG) Log.d(TAG, "close()");
518
519 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700520 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800521 }
522
523 /**
524 * Returns a service by UUID, instance and type.
525 * @hide
526 */
527 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
528 int instanceId, int type) {
529 for(BluetoothGattService svc : mServices) {
530 if (svc.getDevice().equals(device) &&
531 svc.getType() == type &&
532 svc.getInstanceId() == instanceId &&
533 svc.getUuid().equals(uuid)) {
534 return svc;
535 }
536 }
537 return null;
538 }
539
540
541 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700542 * Returns a characteristic with id equal to instanceId.
543 * @hide
544 */
545 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
546 for(BluetoothGattService svc : mServices) {
547 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700548 if (charac.getInstanceId() == instanceId)
549 return charac;
550 }
551 }
552 return null;
553 }
554
555 /**
556 * Returns a descriptor with id equal to instanceId.
557 * @hide
558 */
559 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
560 for(BluetoothGattService svc : mServices) {
561 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
562 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
563 if (desc.getInstanceId() == instanceId)
564 return desc;
565 }
566 }
567 }
568 return null;
569 }
570
571 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800572 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800573 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800574 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
575 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800576 *
577 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
578 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800579 * @param callback GATT callback handler that will receive asynchronous callbacks.
580 * @return If true, the callback will be called to notify success or failure,
581 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800583 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800584 if (DBG) Log.d(TAG, "registerApp()");
585 if (mService == null) return false;
586
587 mCallback = callback;
588 UUID uuid = UUID.randomUUID();
589 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
590
591 try {
592 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
593 } catch (RemoteException e) {
594 Log.e(TAG,"",e);
595 return false;
596 }
597
598 return true;
599 }
600
601 /**
602 * Unregister the current application and callbacks.
603 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800604 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800605 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
606 if (mService == null || mClientIf == 0) return;
607
608 try {
609 mCallback = null;
610 mService.unregisterClient(mClientIf);
611 mClientIf = 0;
612 } catch (RemoteException e) {
613 Log.e(TAG,"",e);
614 }
615 }
616
617 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800618 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800619 *
620 * <p>The connection may not be established right away, but will be
621 * completed when the remote device is available. A
622 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
623 * invoked when the connection state changes as a result of this function.
624 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700625 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800626 * the remote device, or rather passively scan and finalize the connection
627 * when the remote device is in range/available. Generally, the first ever
628 * connection to a device should be direct (autoConnect set to false) and
629 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800630 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800631 *
632 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
633 *
634 * @param device Remote device to connect to
635 * @param autoConnect Whether to directly connect to the remote device (false)
636 * or to automatically connect as soon as the remote
637 * device becomes available (true).
638 * @return true, if the connection attempt was initiated successfully
639 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800640 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
641 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
642 synchronized(mStateLock) {
643 if (mConnState != CONN_STATE_IDLE) {
644 throw new IllegalStateException("Not idle");
645 }
646 mConnState = CONN_STATE_CONNECTING;
647 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700648
649 mAutoConnect = autoConnect;
650
Matthew Xieddf7e472013-03-01 18:41:02 -0800651 if (!registerApp(callback)) {
652 synchronized(mStateLock) {
653 mConnState = CONN_STATE_IDLE;
654 }
655 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800656 return false;
657 }
658
Sungki Kimd35167a2016-05-19 10:18:07 -0700659 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800660 return true;
661 }
662
663 /**
664 * Disconnects an established connection, or cancels a connection attempt
665 * currently in progress.
666 *
667 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800668 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800669 public void disconnect() {
670 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800671 if (mService == null || mClientIf == 0) return;
672
673 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800674 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800675 } catch (RemoteException e) {
676 Log.e(TAG,"",e);
677 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700678 }
679
680 /**
681 * Connect back to remote device.
682 *
683 * <p>This method is used to re-connect to a remote device after the
684 * connection has been dropped. If the device is not in range, the
685 * re-connection will be triggered once the device is back in range.
686 *
687 * @return true, if the connection attempt was initiated successfully
688 */
689 public boolean connect() {
690 try {
691 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700692 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700693 return true;
694 } catch (RemoteException e) {
695 Log.e(TAG,"",e);
696 return false;
697 }
698 }
699
700 /**
701 * Return the remote bluetooth device this GATT client targets to
702 *
703 * @return remote bluetooth device
704 */
705 public BluetoothDevice getDevice() {
706 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800707 }
708
709 /**
710 * Discovers services offered by a remote device as well as their
711 * characteristics and descriptors.
712 *
713 * <p>This is an asynchronous operation. Once service discovery is completed,
714 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
715 * triggered. If the discovery was successful, the remote services can be
716 * retrieved using the {@link #getServices} function.
717 *
718 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
719 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800720 * @return true, if the remote service discovery has been started
721 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800722 public boolean discoverServices() {
723 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800724 if (mService == null || mClientIf == 0) return false;
725
726 mServices.clear();
727
728 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800729 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800730 } catch (RemoteException e) {
731 Log.e(TAG,"",e);
732 return false;
733 }
734
735 return true;
736 }
737
738 /**
739 * Returns a list of GATT services offered by the remote device.
740 *
741 * <p>This function requires that service discovery has been completed
742 * for the given device.
743 *
744 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
745 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800746 * @return List of services on the remote device. Returns an empty list
747 * if service discovery has not yet been performed.
748 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800749 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800750 List<BluetoothGattService> result =
751 new ArrayList<BluetoothGattService>();
752
753 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800754 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800755 result.add(service);
756 }
757 }
758
759 return result;
760 }
761
762 /**
763 * Returns a {@link BluetoothGattService}, if the requested UUID is
764 * supported by the remote device.
765 *
766 * <p>This function requires that service discovery has been completed
767 * for the given device.
768 *
769 * <p>If multiple instances of the same service (as identified by UUID)
770 * exist, the first instance of the service is returned.
771 *
772 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
773 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800774 * @param uuid UUID of the requested service
775 * @return BluetoothGattService if supported, or null if the requested
776 * service is not offered by the remote device.
777 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800778 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800779 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800780 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800781 service.getUuid().equals(uuid)) {
782 return service;
783 }
784 }
785
786 return null;
787 }
788
789 /**
790 * Reads the requested characteristic from the associated remote device.
791 *
792 * <p>This is an asynchronous operation. The result of the read operation
793 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
794 * callback.
795 *
796 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
797 *
798 * @param characteristic Characteristic to read from the remote device
799 * @return true, if the read operation was initiated successfully
800 */
801 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
802 if ((characteristic.getProperties() &
803 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
804
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700805 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 if (mService == null || mClientIf == 0) return false;
807
808 BluetoothGattService service = characteristic.getService();
809 if (service == null) return false;
810
811 BluetoothDevice device = service.getDevice();
812 if (device == null) return false;
813
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700814 synchronized(mDeviceBusy) {
815 if (mDeviceBusy) return false;
816 mDeviceBusy = true;
817 }
818
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800819 try {
820 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700821 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800822 } catch (RemoteException e) {
823 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700824 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800825 return false;
826 }
827
828 return true;
829 }
830
831 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800832 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800833 *
834 * <p>Once the write operation has been completed, the
835 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
836 * reporting the result of the operation.
837 *
838 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
839 *
840 * @param characteristic Characteristic to write on the remote device
841 * @return true, if the write operation was initiated successfully
842 */
843 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
844 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
845 && (characteristic.getProperties() &
846 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
847
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700848 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800849 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800850
851 BluetoothGattService service = characteristic.getService();
852 if (service == null) return false;
853
854 BluetoothDevice device = service.getDevice();
855 if (device == null) return false;
856
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700857 synchronized(mDeviceBusy) {
858 if (mDeviceBusy) return false;
859 mDeviceBusy = true;
860 }
861
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800862 try {
863 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700864 characteristic.getInstanceId(), characteristic.getWriteType(),
865 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800866 } catch (RemoteException e) {
867 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700868 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800869 return false;
870 }
871
872 return true;
873 }
874
875 /**
876 * Reads the value for a given descriptor from the associated remote device.
877 *
878 * <p>Once the read operation has been completed, the
879 * {@link BluetoothGattCallback#onDescriptorRead} callback is
880 * triggered, signaling the result of the operation.
881 *
882 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
883 *
884 * @param descriptor Descriptor value to read from the remote device
885 * @return true, if the read operation was initiated successfully
886 */
887 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700888 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800889 if (mService == null || mClientIf == 0) return false;
890
891 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
892 if (characteristic == null) return false;
893
894 BluetoothGattService service = characteristic.getService();
895 if (service == null) return false;
896
897 BluetoothDevice device = service.getDevice();
898 if (device == null) return false;
899
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700900 synchronized(mDeviceBusy) {
901 if (mDeviceBusy) return false;
902 mDeviceBusy = true;
903 }
904
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800905 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700906 mService.readDescriptor(mClientIf, device.getAddress(),
907 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800908 } catch (RemoteException e) {
909 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700910 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800911 return false;
912 }
913
914 return true;
915 }
916
917 /**
918 * Write the value of a given descriptor to the associated remote device.
919 *
920 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
921 * triggered to report the result of the write operation.
922 *
923 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
924 *
925 * @param descriptor Descriptor to write to the associated remote device
926 * @return true, if the write operation was initiated successfully
927 */
928 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700929 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800930 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800931
932 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
933 if (characteristic == null) return false;
934
935 BluetoothGattService service = characteristic.getService();
936 if (service == null) return false;
937
938 BluetoothDevice device = service.getDevice();
939 if (device == null) return false;
940
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700941 synchronized(mDeviceBusy) {
942 if (mDeviceBusy) return false;
943 mDeviceBusy = true;
944 }
945
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800946 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700947 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Andre Eisenbach613f4302016-03-29 20:52:38 -0700948 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE,
949 descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800950 } catch (RemoteException e) {
951 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700952 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800953 return false;
954 }
955
956 return true;
957 }
958
959 /**
960 * Initiates a reliable write transaction for a given remote device.
961 *
962 * <p>Once a reliable write transaction has been initiated, all calls
963 * to {@link #writeCharacteristic} are sent to the remote device for
964 * verification and queued up for atomic execution. The application will
965 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
966 * in response to every {@link #writeCharacteristic} call and is responsible
967 * for verifying if the value has been transmitted accurately.
968 *
969 * <p>After all characteristics have been queued up and verified,
970 * {@link #executeReliableWrite} will execute all writes. If a characteristic
971 * was not written correctly, calling {@link #abortReliableWrite} will
972 * cancel the current transaction without commiting any values on the
973 * remote device.
974 *
975 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
976 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800977 * @return true, if the reliable write transaction has been initiated
978 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800979 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700980 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800981 if (mService == null || mClientIf == 0) return false;
982
983 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800984 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800985 } catch (RemoteException e) {
986 Log.e(TAG,"",e);
987 return false;
988 }
989
990 return true;
991 }
992
993 /**
994 * Executes a reliable write transaction for a given remote device.
995 *
996 * <p>This function will commit all queued up characteristic write
997 * operations for a given remote device.
998 *
999 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1000 * invoked to indicate whether the transaction has been executed correctly.
1001 *
1002 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1003 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001004 * @return true, if the request to execute the transaction has been sent
1005 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001006 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001007 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001008 if (mService == null || mClientIf == 0) return false;
1009
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001010 synchronized(mDeviceBusy) {
1011 if (mDeviceBusy) return false;
1012 mDeviceBusy = true;
1013 }
1014
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001016 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001017 } catch (RemoteException e) {
1018 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001019 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001020 return false;
1021 }
1022
1023 return true;
1024 }
1025
1026 /**
1027 * Cancels a reliable write transaction for a given device.
1028 *
1029 * <p>Calling this function will discard all queued characteristic write
1030 * operations for a given remote device.
1031 *
1032 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001033 */
John Du48f8b5d2013-08-19 12:20:37 -07001034 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001035 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001036 if (mService == null || mClientIf == 0) return;
1037
1038 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001039 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001040 } catch (RemoteException e) {
1041 Log.e(TAG,"",e);
1042 }
1043 }
1044
1045 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001046 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001047 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001048 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001049 public void abortReliableWrite(BluetoothDevice mDevice) {
1050 abortReliableWrite();
1051 }
1052
1053 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001054 * Enable or disable notifications/indications for a given characteristic.
1055 *
1056 * <p>Once notifications are enabled for a characteristic, a
1057 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1058 * triggered if the remote device indicates that the given characteristic
1059 * has changed.
1060 *
1061 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1062 *
1063 * @param characteristic The characteristic for which to enable notifications
1064 * @param enable Set to true to enable notifications/indications
1065 * @return true, if the requested notification status was set successfully
1066 */
1067 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1068 boolean enable) {
1069 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1070 + " enable: " + enable);
1071 if (mService == null || mClientIf == 0) return false;
1072
1073 BluetoothGattService service = characteristic.getService();
1074 if (service == null) return false;
1075
1076 BluetoothDevice device = service.getDevice();
1077 if (device == null) return false;
1078
1079 try {
1080 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001081 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001082 } catch (RemoteException e) {
1083 Log.e(TAG,"",e);
1084 return false;
1085 }
1086
1087 return true;
1088 }
1089
1090 /**
1091 * Clears the internal cache and forces a refresh of the services from the
1092 * remote device.
1093 * @hide
1094 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001095 public boolean refresh() {
1096 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001097 if (mService == null || mClientIf == 0) return false;
1098
1099 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001100 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001101 } catch (RemoteException e) {
1102 Log.e(TAG,"",e);
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109 /**
1110 * Read the RSSI for a connected remote device.
1111 *
1112 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1113 * invoked when the RSSI value has been read.
1114 *
1115 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1116 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001117 * @return true, if the RSSI value has been requested successfully
1118 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001119 public boolean readRemoteRssi() {
1120 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001121 if (mService == null || mClientIf == 0) return false;
1122
1123 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001124 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001125 } catch (RemoteException e) {
1126 Log.e(TAG,"",e);
1127 return false;
1128 }
1129
1130 return true;
1131 }
1132
1133 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001134 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001135 *
1136 * <p>When performing a write request operation (write without response),
1137 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001138 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001139 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001140 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001141 * whether this operation was successful.
1142 *
1143 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1144 *
1145 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001146 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001147 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001148 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1149 + " mtu: " + mtu);
1150 if (mService == null || mClientIf == 0) return false;
1151
1152 try {
1153 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1154 } catch (RemoteException e) {
1155 Log.e(TAG,"",e);
1156 return false;
1157 }
1158
1159 return true;
1160 }
1161
1162 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001163 * Request a connection parameter update.
1164 *
1165 * <p>This function will send a connection parameter update request to the
1166 * remote device.
1167 *
1168 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001169 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1170 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1171 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001172 * @throws IllegalArgumentException If the parameters are outside of their
1173 * specified range.
1174 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001175 public boolean requestConnectionPriority(int connectionPriority) {
1176 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1177 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001178 throw new IllegalArgumentException("connectionPriority not within valid range");
1179 }
1180
Andre Eisenbach4072da02014-08-19 17:58:55 -07001181 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001182 if (mService == null || mClientIf == 0) return false;
1183
1184 try {
1185 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1186 } catch (RemoteException e) {
1187 Log.e(TAG,"",e);
1188 return false;
1189 }
1190
1191 return true;
1192 }
1193
1194 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001195 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1196 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001197 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001198 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001199 */
1200 @Override
1201 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001202 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001203 }
1204
1205 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001206 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1207 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001208 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001209 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001210 */
1211 @Override
1212 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001213 throw new UnsupportedOperationException
1214 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001215 }
1216
1217 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001218 * Not supported - please use
1219 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1220 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001221 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001222 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001223 */
1224 @Override
1225 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001226 throw new UnsupportedOperationException
1227 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001228 }
1229}