blob: 552c8d3b0143dc390d4bd17298aae4c4d9a7582d [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
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044 private IBluetoothGatt mService;
45 private BluetoothGattCallback mCallback;
46 private int mClientIf;
47 private boolean mAuthRetry = false;
Matthew Xieddf7e472013-03-01 18:41:02 -080048 private BluetoothDevice mDevice;
49 private boolean mAutoConnect;
50 private int mConnState;
51 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070052 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070053 private int mTransport;
Matthew Xieddf7e472013-03-01 18:41:02 -080054
55 private static final int CONN_STATE_IDLE = 0;
56 private static final int CONN_STATE_CONNECTING = 1;
57 private static final int CONN_STATE_CONNECTED = 2;
58 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070059 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080060
61 private List<BluetoothGattService> mServices;
62
Matthew Xieddf7e472013-03-01 18:41:02 -080063 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080064 public static final int GATT_SUCCESS = 0;
65
Matthew Xieddf7e472013-03-01 18:41:02 -080066 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080067 public static final int GATT_READ_NOT_PERMITTED = 0x2;
68
Matthew Xieddf7e472013-03-01 18:41:02 -080069 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080070 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
71
72 /** Insufficient authentication for a given operation */
73 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
74
75 /** The given request is not supported */
76 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
77
78 /** Insufficient encryption for a given operation */
79 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
80
81 /** A read or write operation was requested with an invalid offset */
82 public static final int GATT_INVALID_OFFSET = 0x7;
83
84 /** A write operation exceeds the maximum length of the attribute */
85 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
86
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070087 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070088 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
89
Matthew Xie90ca8072013-05-28 21:06:50 +000090 /** A GATT operation failed, errors other than the above */
91 public static final int GATT_FAILURE = 0x101;
92
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080093 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070094 * Connection paramter update - Use the connection paramters recommended by the
95 * Bluetooth SIG. This is the default value if no connection parameter update
96 * is requested.
97 */
Andre Eisenbach4072da02014-08-19 17:58:55 -070098 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070099
100 /**
101 * Connection paramter update - Request a high priority, low latency connection.
102 * An application should only request high priority connection paramters to transfer
103 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700104 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700105 * to reduce energy use.
106 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700107 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700108
109 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700110 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700111
112 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800113 * No authentication required.
114 * @hide
115 */
116 /*package*/ static final int AUTHENTICATION_NONE = 0;
117
118 /**
119 * Authentication requested; no man-in-the-middle protection required.
120 * @hide
121 */
122 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
123
124 /**
125 * Authentication with man-in-the-middle protection requested.
126 * @hide
127 */
128 /*package*/ static final int AUTHENTICATION_MITM = 2;
129
130 /**
Wei Wange0d4afb2014-07-29 21:34:25 -0700131 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800132 */
133 private final IBluetoothGattCallback mBluetoothGattCallback =
Wei Wange0d4afb2014-07-29 21:34:25 -0700134 new BluetoothGattCallbackWrapper() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800135 /**
136 * Application interface registered - app is ready to go
137 * @hide
138 */
139 public void onClientRegistered(int status, int clientIf) {
140 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
141 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800142 if (VDBG) {
143 synchronized(mStateLock) {
144 if (mConnState != CONN_STATE_CONNECTING) {
145 Log.e(TAG, "Bad connection state: " + mConnState);
146 }
147 }
148 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800149 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800150 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700151 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800152 BluetoothProfile.STATE_DISCONNECTED);
153 synchronized(mStateLock) {
154 mConnState = CONN_STATE_IDLE;
155 }
156 return;
157 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800158 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800159 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700160 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800161 } catch (RemoteException e) {
162 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800163 }
164 }
165
166 /**
167 * Client connection state changed
168 * @hide
169 */
170 public void onClientConnectionState(int status, int clientIf,
171 boolean connected, String address) {
172 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
173 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800174 if (!address.equals(mDevice.getAddress())) {
175 return;
176 }
177 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
178 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800179 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700180 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800181 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700182 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800183 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800184
185 synchronized(mStateLock) {
186 if (connected) {
187 mConnState = CONN_STATE_CONNECTED;
188 } else {
189 mConnState = CONN_STATE_IDLE;
190 }
191 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700192
193 synchronized(mDeviceBusy) {
194 mDeviceBusy = false;
195 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800196 }
197
198 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800199 * Remote search has been completed.
200 * The internal object structure should now reflect the state
201 * of the remote device database. Let the application know that
202 * we are done at this point.
203 * @hide
204 */
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800205 public void onSearchComplete(String address, List<BluetoothGattService> services,
206 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800207 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800208 if (!address.equals(mDevice.getAddress())) {
209 return;
210 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800211
212 for (BluetoothGattService s : services) {
213 //services we receive don't have device set properly.
214 s.setDevice(mDevice);
215 }
216
217 mServices.addAll(services);
218
219 // Fix references to included services, as they doesn't point to right objects.
220 for (BluetoothGattService fixedService : mServices) {
221 ArrayList<BluetoothGattService> includedServices =
222 new ArrayList(fixedService.getIncludedServices());
223 fixedService.getIncludedServices().clear();
224
225 for(BluetoothGattService brokenRef : includedServices) {
226 BluetoothGattService includedService = getService(mDevice,
227 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
228 if (includedService != null) {
229 fixedService.addIncludedService(includedService);
230 } else {
231 Log.e(TAG, "Broken GATT database: can't find included service.");
232 }
233 }
234 }
235
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800236 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700237 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800238 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700239 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800240 }
241 }
242
243 /**
244 * Remote characteristic has been read.
245 * Updates the internal value.
246 * @hide
247 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700248 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700249 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700250 + " handle=" + handle + " Status=" + status);
251
252 Log.w(TAG, "onCharacteristicRead() - Device=" + address
253 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800254
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;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700268 mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800269 return;
270 } catch (RemoteException e) {
271 Log.e(TAG,"",e);
272 }
273 }
274
275 mAuthRetry = false;
276
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700277 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
278 if (characteristic == null) {
279 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
280 return;
281 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800282
283 if (status == 0) characteristic.setValue(value);
284
285 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700286 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800287 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700288 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800289 }
290 }
291
292 /**
293 * Characteristic has been written to the remote device.
294 * Let the app know how we did...
295 * @hide
296 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700297 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700298 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700299 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800300
Matthew Xieddf7e472013-03-01 18:41:02 -0800301 if (!address.equals(mDevice.getAddress())) {
302 return;
303 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700304
305 synchronized(mDeviceBusy) {
306 mDeviceBusy = false;
307 }
308
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700309 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800310 if (characteristic == null) return;
311
312 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
313 || status == GATT_INSUFFICIENT_ENCRYPTION)
314 && mAuthRetry == false) {
315 try {
316 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700317 mService.writeCharacteristic(mClientIf, address, handle,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800318 characteristic.getWriteType(), AUTHENTICATION_MITM,
319 characteristic.getValue());
320 return;
321 } catch (RemoteException e) {
322 Log.e(TAG,"",e);
323 }
324 }
325
326 mAuthRetry = false;
327
328 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700329 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800330 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700331 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800332 }
333 }
334
335 /**
336 * Remote characteristic has been updated.
337 * Updates the internal value.
338 * @hide
339 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700340 public void onNotify(String address, int handle, byte[] value) {
341 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800342
Matthew Xieddf7e472013-03-01 18:41:02 -0800343 if (!address.equals(mDevice.getAddress())) {
344 return;
345 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700347 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800348 if (characteristic == null) return;
349
350 characteristic.setValue(value);
351
352 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700353 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800354 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700355 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800356 }
357 }
358
359 /**
360 * Descriptor has been read.
361 * @hide
362 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700363 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
364 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800365
Matthew Xieddf7e472013-03-01 18:41:02 -0800366 if (!address.equals(mDevice.getAddress())) {
367 return;
368 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700369
370 synchronized(mDeviceBusy) {
371 mDeviceBusy = false;
372 }
373
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700374 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800375 if (descriptor == null) return;
376
377 if (status == 0) descriptor.setValue(value);
378
379 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
380 || status == GATT_INSUFFICIENT_ENCRYPTION)
381 && mAuthRetry == false) {
382 try {
383 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700384 mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM);
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700385 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800386 } catch (RemoteException e) {
387 Log.e(TAG,"",e);
388 }
389 }
390
391 mAuthRetry = true;
392
393 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700394 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800395 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700396 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800397 }
398 }
399
400 /**
401 * Descriptor write operation complete.
402 * @hide
403 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700404 public void onDescriptorWrite(String address, int status, int handle) {
405 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800406
Matthew Xieddf7e472013-03-01 18:41:02 -0800407 if (!address.equals(mDevice.getAddress())) {
408 return;
409 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700410
411 synchronized(mDeviceBusy) {
412 mDeviceBusy = false;
413 }
414
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700415 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800416 if (descriptor == null) return;
417
418 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
419 || status == GATT_INSUFFICIENT_ENCRYPTION)
420 && mAuthRetry == false) {
421 try {
422 mAuthRetry = true;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700423 mService.writeDescriptor(mClientIf, address, handle,
Andre Eisenbach613f4302016-03-29 20:52:38 -0700424 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800425 AUTHENTICATION_MITM, descriptor.getValue());
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700426 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800427 } catch (RemoteException e) {
428 Log.e(TAG,"",e);
429 }
430 }
431
432 mAuthRetry = false;
433
434 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700435 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800436 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700437 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800438 }
439 }
440
441 /**
442 * Prepared write transaction completed (or aborted)
443 * @hide
444 */
445 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700446 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800447 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800448 if (!address.equals(mDevice.getAddress())) {
449 return;
450 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700451
452 synchronized(mDeviceBusy) {
453 mDeviceBusy = false;
454 }
455
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800456 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700457 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800458 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700459 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800460 }
461 }
462
463 /**
464 * Remote device RSSI has been read
465 * @hide
466 */
467 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700468 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800469 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800470 if (!address.equals(mDevice.getAddress())) {
471 return;
472 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800473 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700474 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800475 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700476 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800477 }
478 }
Wei Wangf3055892014-03-11 22:22:41 -0700479
480 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700481 * Callback invoked when the MTU for a given connection changes
482 * @hide
483 */
484 public void onConfigureMTU(String address, int mtu, int status) {
485 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
486 " mtu=" + mtu + " status=" + status);
487 if (!address.equals(mDevice.getAddress())) {
488 return;
489 }
490 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700491 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700492 } catch (Exception ex) {
493 Log.w(TAG, "Unhandled exception in callback", ex);
494 }
Wei Wangf3055892014-03-11 22:22:41 -0700495 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800496 };
497
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700498 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700499 int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800500 mService = iGatt;
501 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700502 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800503 mServices = new ArrayList<BluetoothGattService>();
504
Matthew Xieddf7e472013-03-01 18:41:02 -0800505 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800506 }
507
508 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700509 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700510 *
511 * Application should call this method as early as possible after it is done with
512 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800513 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700514 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800515 if (DBG) Log.d(TAG, "close()");
516
517 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700518 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 }
520
521 /**
522 * Returns a service by UUID, instance and type.
523 * @hide
524 */
525 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
526 int instanceId, int type) {
527 for(BluetoothGattService svc : mServices) {
528 if (svc.getDevice().equals(device) &&
529 svc.getType() == type &&
530 svc.getInstanceId() == instanceId &&
531 svc.getUuid().equals(uuid)) {
532 return svc;
533 }
534 }
535 return null;
536 }
537
538
539 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700540 * Returns a characteristic with id equal to instanceId.
541 * @hide
542 */
543 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
544 for(BluetoothGattService svc : mServices) {
545 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700546 if (charac.getInstanceId() == instanceId)
547 return charac;
548 }
549 }
550 return null;
551 }
552
553 /**
554 * Returns a descriptor with id equal to instanceId.
555 * @hide
556 */
557 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
558 for(BluetoothGattService svc : mServices) {
559 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
560 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
561 if (desc.getInstanceId() == instanceId)
562 return desc;
563 }
564 }
565 }
566 return null;
567 }
568
569 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800570 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800571 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800572 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
573 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800574 *
575 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
576 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800577 * @param callback GATT callback handler that will receive asynchronous callbacks.
578 * @return If true, the callback will be called to notify success or failure,
579 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800581 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 if (DBG) Log.d(TAG, "registerApp()");
583 if (mService == null) return false;
584
585 mCallback = callback;
586 UUID uuid = UUID.randomUUID();
587 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
588
589 try {
590 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
591 } catch (RemoteException e) {
592 Log.e(TAG,"",e);
593 return false;
594 }
595
596 return true;
597 }
598
599 /**
600 * Unregister the current application and callbacks.
601 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800602 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800603 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
604 if (mService == null || mClientIf == 0) return;
605
606 try {
607 mCallback = null;
608 mService.unregisterClient(mClientIf);
609 mClientIf = 0;
610 } catch (RemoteException e) {
611 Log.e(TAG,"",e);
612 }
613 }
614
615 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800616 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800617 *
618 * <p>The connection may not be established right away, but will be
619 * completed when the remote device is available. A
620 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
621 * invoked when the connection state changes as a result of this function.
622 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700623 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800624 * the remote device, or rather passively scan and finalize the connection
625 * when the remote device is in range/available. Generally, the first ever
626 * connection to a device should be direct (autoConnect set to false) and
627 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800628 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800629 *
630 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
631 *
632 * @param device Remote device to connect to
633 * @param autoConnect Whether to directly connect to the remote device (false)
634 * or to automatically connect as soon as the remote
635 * device becomes available (true).
636 * @return true, if the connection attempt was initiated successfully
637 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800638 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
639 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
640 synchronized(mStateLock) {
641 if (mConnState != CONN_STATE_IDLE) {
642 throw new IllegalStateException("Not idle");
643 }
644 mConnState = CONN_STATE_CONNECTING;
645 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700646
647 mAutoConnect = autoConnect;
648
Matthew Xieddf7e472013-03-01 18:41:02 -0800649 if (!registerApp(callback)) {
650 synchronized(mStateLock) {
651 mConnState = CONN_STATE_IDLE;
652 }
653 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800654 return false;
655 }
656
Sungki Kimd35167a2016-05-19 10:18:07 -0700657 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800658 return true;
659 }
660
661 /**
662 * Disconnects an established connection, or cancels a connection attempt
663 * currently in progress.
664 *
665 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800666 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800667 public void disconnect() {
668 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800669 if (mService == null || mClientIf == 0) return;
670
671 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800672 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800673 } catch (RemoteException e) {
674 Log.e(TAG,"",e);
675 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700676 }
677
678 /**
679 * Connect back to remote device.
680 *
681 * <p>This method is used to re-connect to a remote device after the
682 * connection has been dropped. If the device is not in range, the
683 * re-connection will be triggered once the device is back in range.
684 *
685 * @return true, if the connection attempt was initiated successfully
686 */
687 public boolean connect() {
688 try {
689 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700690 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700691 return true;
692 } catch (RemoteException e) {
693 Log.e(TAG,"",e);
694 return false;
695 }
696 }
697
698 /**
699 * Return the remote bluetooth device this GATT client targets to
700 *
701 * @return remote bluetooth device
702 */
703 public BluetoothDevice getDevice() {
704 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800705 }
706
707 /**
708 * Discovers services offered by a remote device as well as their
709 * characteristics and descriptors.
710 *
711 * <p>This is an asynchronous operation. Once service discovery is completed,
712 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
713 * triggered. If the discovery was successful, the remote services can be
714 * retrieved using the {@link #getServices} function.
715 *
716 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
717 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800718 * @return true, if the remote service discovery has been started
719 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800720 public boolean discoverServices() {
721 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800722 if (mService == null || mClientIf == 0) return false;
723
724 mServices.clear();
725
726 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800727 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800728 } catch (RemoteException e) {
729 Log.e(TAG,"",e);
730 return false;
731 }
732
733 return true;
734 }
735
736 /**
737 * Returns a list of GATT services offered by the remote device.
738 *
739 * <p>This function requires that service discovery has been completed
740 * for the given device.
741 *
742 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
743 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800744 * @return List of services on the remote device. Returns an empty list
745 * if service discovery has not yet been performed.
746 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800747 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800748 List<BluetoothGattService> result =
749 new ArrayList<BluetoothGattService>();
750
751 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800752 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800753 result.add(service);
754 }
755 }
756
757 return result;
758 }
759
760 /**
761 * Returns a {@link BluetoothGattService}, if the requested UUID is
762 * supported by the remote device.
763 *
764 * <p>This function requires that service discovery has been completed
765 * for the given device.
766 *
767 * <p>If multiple instances of the same service (as identified by UUID)
768 * exist, the first instance of the service is returned.
769 *
770 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
771 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800772 * @param uuid UUID of the requested service
773 * @return BluetoothGattService if supported, or null if the requested
774 * service is not offered by the remote device.
775 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800776 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800777 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800778 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800779 service.getUuid().equals(uuid)) {
780 return service;
781 }
782 }
783
784 return null;
785 }
786
787 /**
788 * Reads the requested characteristic from the associated remote device.
789 *
790 * <p>This is an asynchronous operation. The result of the read operation
791 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
792 * callback.
793 *
794 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
795 *
796 * @param characteristic Characteristic to read from the remote device
797 * @return true, if the read operation was initiated successfully
798 */
799 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
800 if ((characteristic.getProperties() &
801 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
802
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700803 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800804 if (mService == null || mClientIf == 0) return false;
805
806 BluetoothGattService service = characteristic.getService();
807 if (service == null) return false;
808
809 BluetoothDevice device = service.getDevice();
810 if (device == null) return false;
811
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700812 synchronized(mDeviceBusy) {
813 if (mDeviceBusy) return false;
814 mDeviceBusy = true;
815 }
816
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800817 try {
818 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700819 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800820 } catch (RemoteException e) {
821 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700822 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800823 return false;
824 }
825
826 return true;
827 }
828
829 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800830 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800831 *
832 * <p>Once the write operation has been completed, the
833 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
834 * reporting the result of the operation.
835 *
836 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
837 *
838 * @param characteristic Characteristic to write on the remote device
839 * @return true, if the write operation was initiated successfully
840 */
841 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
842 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
843 && (characteristic.getProperties() &
844 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
845
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700846 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800847 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800848
849 BluetoothGattService service = characteristic.getService();
850 if (service == null) return false;
851
852 BluetoothDevice device = service.getDevice();
853 if (device == null) return false;
854
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700855 synchronized(mDeviceBusy) {
856 if (mDeviceBusy) return false;
857 mDeviceBusy = true;
858 }
859
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800860 try {
861 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700862 characteristic.getInstanceId(), characteristic.getWriteType(),
863 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800864 } catch (RemoteException e) {
865 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700866 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800867 return false;
868 }
869
870 return true;
871 }
872
873 /**
874 * Reads the value for a given descriptor from the associated remote device.
875 *
876 * <p>Once the read operation has been completed, the
877 * {@link BluetoothGattCallback#onDescriptorRead} callback is
878 * triggered, signaling the result of the operation.
879 *
880 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
881 *
882 * @param descriptor Descriptor value to read from the remote device
883 * @return true, if the read operation was initiated successfully
884 */
885 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700886 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800887 if (mService == null || mClientIf == 0) return false;
888
889 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
890 if (characteristic == null) return false;
891
892 BluetoothGattService service = characteristic.getService();
893 if (service == null) return false;
894
895 BluetoothDevice device = service.getDevice();
896 if (device == null) return false;
897
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700898 synchronized(mDeviceBusy) {
899 if (mDeviceBusy) return false;
900 mDeviceBusy = true;
901 }
902
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800903 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700904 mService.readDescriptor(mClientIf, device.getAddress(),
905 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800906 } catch (RemoteException e) {
907 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700908 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800909 return false;
910 }
911
912 return true;
913 }
914
915 /**
916 * Write the value of a given descriptor to the associated remote device.
917 *
918 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
919 * triggered to report the result of the write operation.
920 *
921 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
922 *
923 * @param descriptor Descriptor to write to the associated remote device
924 * @return true, if the write operation was initiated successfully
925 */
926 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700927 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800928 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800929
930 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
931 if (characteristic == null) return false;
932
933 BluetoothGattService service = characteristic.getService();
934 if (service == null) return false;
935
936 BluetoothDevice device = service.getDevice();
937 if (device == null) return false;
938
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700939 synchronized(mDeviceBusy) {
940 if (mDeviceBusy) return false;
941 mDeviceBusy = true;
942 }
943
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800944 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700945 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Andre Eisenbach613f4302016-03-29 20:52:38 -0700946 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE,
947 descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800948 } catch (RemoteException e) {
949 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700950 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800951 return false;
952 }
953
954 return true;
955 }
956
957 /**
958 * Initiates a reliable write transaction for a given remote device.
959 *
960 * <p>Once a reliable write transaction has been initiated, all calls
961 * to {@link #writeCharacteristic} are sent to the remote device for
962 * verification and queued up for atomic execution. The application will
963 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
964 * in response to every {@link #writeCharacteristic} call and is responsible
965 * for verifying if the value has been transmitted accurately.
966 *
967 * <p>After all characteristics have been queued up and verified,
968 * {@link #executeReliableWrite} will execute all writes. If a characteristic
969 * was not written correctly, calling {@link #abortReliableWrite} will
970 * cancel the current transaction without commiting any values on the
971 * remote device.
972 *
973 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
974 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800975 * @return true, if the reliable write transaction has been initiated
976 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800977 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700978 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800979 if (mService == null || mClientIf == 0) return false;
980
981 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800982 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800983 } catch (RemoteException e) {
984 Log.e(TAG,"",e);
985 return false;
986 }
987
988 return true;
989 }
990
991 /**
992 * Executes a reliable write transaction for a given remote device.
993 *
994 * <p>This function will commit all queued up characteristic write
995 * operations for a given remote device.
996 *
997 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
998 * invoked to indicate whether the transaction has been executed correctly.
999 *
1000 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1001 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001002 * @return true, if the request to execute the transaction has been sent
1003 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001004 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001005 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001006 if (mService == null || mClientIf == 0) return false;
1007
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001008 synchronized(mDeviceBusy) {
1009 if (mDeviceBusy) return false;
1010 mDeviceBusy = true;
1011 }
1012
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001013 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001014 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 } catch (RemoteException e) {
1016 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001017 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001018 return false;
1019 }
1020
1021 return true;
1022 }
1023
1024 /**
1025 * Cancels a reliable write transaction for a given device.
1026 *
1027 * <p>Calling this function will discard all queued characteristic write
1028 * operations for a given remote device.
1029 *
1030 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001031 */
John Du48f8b5d2013-08-19 12:20:37 -07001032 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001033 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001034 if (mService == null || mClientIf == 0) return;
1035
1036 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001037 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001038 } catch (RemoteException e) {
1039 Log.e(TAG,"",e);
1040 }
1041 }
1042
1043 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001044 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001045 */
1046 public void abortReliableWrite(BluetoothDevice mDevice) {
1047 abortReliableWrite();
1048 }
1049
1050 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001051 * Enable or disable notifications/indications for a given characteristic.
1052 *
1053 * <p>Once notifications are enabled for a characteristic, a
1054 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1055 * triggered if the remote device indicates that the given characteristic
1056 * has changed.
1057 *
1058 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1059 *
1060 * @param characteristic The characteristic for which to enable notifications
1061 * @param enable Set to true to enable notifications/indications
1062 * @return true, if the requested notification status was set successfully
1063 */
1064 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1065 boolean enable) {
1066 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1067 + " enable: " + enable);
1068 if (mService == null || mClientIf == 0) return false;
1069
1070 BluetoothGattService service = characteristic.getService();
1071 if (service == null) return false;
1072
1073 BluetoothDevice device = service.getDevice();
1074 if (device == null) return false;
1075
1076 try {
1077 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001078 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001079 } catch (RemoteException e) {
1080 Log.e(TAG,"",e);
1081 return false;
1082 }
1083
1084 return true;
1085 }
1086
1087 /**
1088 * Clears the internal cache and forces a refresh of the services from the
1089 * remote device.
1090 * @hide
1091 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001092 public boolean refresh() {
1093 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001094 if (mService == null || mClientIf == 0) return false;
1095
1096 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001097 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001098 } catch (RemoteException e) {
1099 Log.e(TAG,"",e);
1100 return false;
1101 }
1102
1103 return true;
1104 }
1105
1106 /**
1107 * Read the RSSI for a connected remote device.
1108 *
1109 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1110 * invoked when the RSSI value has been read.
1111 *
1112 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1113 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001114 * @return true, if the RSSI value has been requested successfully
1115 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001116 public boolean readRemoteRssi() {
1117 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001118 if (mService == null || mClientIf == 0) return false;
1119
1120 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001121 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001122 } catch (RemoteException e) {
1123 Log.e(TAG,"",e);
1124 return false;
1125 }
1126
1127 return true;
1128 }
1129
1130 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001131 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001132 *
1133 * <p>When performing a write request operation (write without response),
1134 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001135 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001136 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001137 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001138 * whether this operation was successful.
1139 *
1140 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1141 *
1142 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001143 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001144 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001145 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1146 + " mtu: " + mtu);
1147 if (mService == null || mClientIf == 0) return false;
1148
1149 try {
1150 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1151 } catch (RemoteException e) {
1152 Log.e(TAG,"",e);
1153 return false;
1154 }
1155
1156 return true;
1157 }
1158
1159 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001160 * Request a connection parameter update.
1161 *
1162 * <p>This function will send a connection parameter update request to the
1163 * remote device.
1164 *
1165 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001166 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1167 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1168 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001169 * @throws IllegalArgumentException If the parameters are outside of their
1170 * specified range.
1171 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001172 public boolean requestConnectionPriority(int connectionPriority) {
1173 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1174 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001175 throw new IllegalArgumentException("connectionPriority not within valid range");
1176 }
1177
Andre Eisenbach4072da02014-08-19 17:58:55 -07001178 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001179 if (mService == null || mClientIf == 0) return false;
1180
1181 try {
1182 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1183 } catch (RemoteException e) {
1184 Log.e(TAG,"",e);
1185 return false;
1186 }
1187
1188 return true;
1189 }
1190
1191 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001192 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1193 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001194 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001195 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001196 */
1197 @Override
1198 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001199 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001200 }
1201
1202 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001203 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1204 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001205 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001206 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001207 */
1208 @Override
1209 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001210 throw new UnsupportedOperationException
1211 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001212 }
1213
1214 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001215 * Not supported - please use
1216 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1217 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001218 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001219 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001220 */
1221 @Override
1222 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001223 throw new UnsupportedOperationException
1224 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001225 }
1226}