blob: 9cfe4175b60463da94b47ab0cdbd18be110530b1 [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 Pawlowski8d312a82016-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 Pawlowski8d312a82016-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 Pawlowskia9e27ec2016-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 Pawlowskia9e27ec2016-03-17 16:00:16 -0700251 + " handle=" + handle + " Status=" + status);
252
Matthew Xieddf7e472013-03-01 18:41:02 -0800253 if (!address.equals(mDevice.getAddress())) {
254 return;
255 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700256
257 synchronized(mDeviceBusy) {
258 mDeviceBusy = false;
259 }
260
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800261 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
262 || status == GATT_INSUFFICIENT_ENCRYPTION)
263 && mAuthRetry == false) {
264 try {
265 mAuthRetry = true;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700266 mService.readCharacteristic(mClientIf, address, handle, AUTHENTICATION_MITM);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800267 return;
268 } catch (RemoteException e) {
269 Log.e(TAG,"",e);
270 }
271 }
272
273 mAuthRetry = false;
274
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700275 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
276 if (characteristic == null) {
277 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
278 return;
279 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800280
281 if (status == 0) characteristic.setValue(value);
282
283 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700284 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800285 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700286 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800287 }
288 }
289
290 /**
291 * Characteristic has been written to the remote device.
292 * Let the app know how we did...
293 * @hide
294 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700295 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700296 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700297 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800298
Matthew Xieddf7e472013-03-01 18:41:02 -0800299 if (!address.equals(mDevice.getAddress())) {
300 return;
301 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700302
303 synchronized(mDeviceBusy) {
304 mDeviceBusy = false;
305 }
306
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700307 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800308 if (characteristic == null) return;
309
310 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
311 || status == GATT_INSUFFICIENT_ENCRYPTION)
312 && mAuthRetry == false) {
313 try {
314 mAuthRetry = true;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700315 mService.writeCharacteristic(mClientIf, address, handle,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 characteristic.getWriteType(), AUTHENTICATION_MITM,
317 characteristic.getValue());
318 return;
319 } catch (RemoteException e) {
320 Log.e(TAG,"",e);
321 }
322 }
323
324 mAuthRetry = false;
325
326 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700327 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800328 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700329 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800330 }
331 }
332
333 /**
334 * Remote characteristic has been updated.
335 * Updates the internal value.
336 * @hide
337 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700338 public void onNotify(String address, int handle, byte[] value) {
339 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800340
Matthew Xieddf7e472013-03-01 18:41:02 -0800341 if (!address.equals(mDevice.getAddress())) {
342 return;
343 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800344
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700345 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346 if (characteristic == null) return;
347
348 characteristic.setValue(value);
349
350 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700351 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800352 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700353 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800354 }
355 }
356
357 /**
358 * Descriptor has been read.
359 * @hide
360 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700361 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
362 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800363
Matthew Xieddf7e472013-03-01 18:41:02 -0800364 if (!address.equals(mDevice.getAddress())) {
365 return;
366 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700367
368 synchronized(mDeviceBusy) {
369 mDeviceBusy = false;
370 }
371
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700372 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800373 if (descriptor == null) return;
374
375 if (status == 0) descriptor.setValue(value);
376
377 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
378 || status == GATT_INSUFFICIENT_ENCRYPTION)
379 && mAuthRetry == false) {
380 try {
381 mAuthRetry = true;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700382 mService.readDescriptor(mClientIf, address, handle, AUTHENTICATION_MITM);
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700383 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800384 } catch (RemoteException e) {
385 Log.e(TAG,"",e);
386 }
387 }
388
389 mAuthRetry = true;
390
391 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700392 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800393 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700394 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800395 }
396 }
397
398 /**
399 * Descriptor write operation complete.
400 * @hide
401 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700402 public void onDescriptorWrite(String address, int status, int handle) {
403 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800404
Matthew Xieddf7e472013-03-01 18:41:02 -0800405 if (!address.equals(mDevice.getAddress())) {
406 return;
407 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700408
409 synchronized(mDeviceBusy) {
410 mDeviceBusy = false;
411 }
412
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700413 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800414 if (descriptor == null) return;
415
416 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
417 || status == GATT_INSUFFICIENT_ENCRYPTION)
418 && mAuthRetry == false) {
419 try {
420 mAuthRetry = true;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700421 mService.writeDescriptor(mClientIf, address, handle,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800422 AUTHENTICATION_MITM, descriptor.getValue());
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700423 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800424 } catch (RemoteException e) {
425 Log.e(TAG,"",e);
426 }
427 }
428
429 mAuthRetry = false;
430
431 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700432 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800433 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700434 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800435 }
436 }
437
438 /**
439 * Prepared write transaction completed (or aborted)
440 * @hide
441 */
442 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700443 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800444 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800445 if (!address.equals(mDevice.getAddress())) {
446 return;
447 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700448
449 synchronized(mDeviceBusy) {
450 mDeviceBusy = false;
451 }
452
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800453 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700454 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800455 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700456 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800457 }
458 }
459
460 /**
461 * Remote device RSSI has been read
462 * @hide
463 */
464 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700465 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800466 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800467 if (!address.equals(mDevice.getAddress())) {
468 return;
469 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800470 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700471 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800472 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700473 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800474 }
475 }
Wei Wangf3055892014-03-11 22:22:41 -0700476
477 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700478 * Callback invoked when the MTU for a given connection changes
479 * @hide
480 */
481 public void onConfigureMTU(String address, int mtu, int status) {
482 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
483 " mtu=" + mtu + " status=" + status);
484 if (!address.equals(mDevice.getAddress())) {
485 return;
486 }
487 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700488 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700489 } catch (Exception ex) {
490 Log.w(TAG, "Unhandled exception in callback", ex);
491 }
Wei Wangf3055892014-03-11 22:22:41 -0700492 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800493 };
494
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700495 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
496 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800497 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800498 mService = iGatt;
499 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700500 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800501 mServices = new ArrayList<BluetoothGattService>();
502
Matthew Xieddf7e472013-03-01 18:41:02 -0800503 mConnState = CONN_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504 }
505
506 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700507 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700508 *
509 * Application should call this method as early as possible after it is done with
510 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800511 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700512 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800513 if (DBG) Log.d(TAG, "close()");
514
515 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700516 mConnState = CONN_STATE_CLOSED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800517 }
518
519 /**
520 * Returns a service by UUID, instance and type.
521 * @hide
522 */
523 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
524 int instanceId, int type) {
525 for(BluetoothGattService svc : mServices) {
526 if (svc.getDevice().equals(device) &&
527 svc.getType() == type &&
528 svc.getInstanceId() == instanceId &&
529 svc.getUuid().equals(uuid)) {
530 return svc;
531 }
532 }
533 return null;
534 }
535
536
537 /**
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700538 * Returns a characteristic with id equal to instanceId.
539 * @hide
540 */
541 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
542 for(BluetoothGattService svc : mServices) {
543 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700544 if (charac.getInstanceId() == instanceId)
545 return charac;
546 }
547 }
548 return null;
549 }
550
551 /**
552 * Returns a descriptor with id equal to instanceId.
553 * @hide
554 */
555 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
556 for(BluetoothGattService svc : mServices) {
557 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
558 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
559 if (desc.getInstanceId() == instanceId)
560 return desc;
561 }
562 }
563 }
564 return null;
565 }
566
567 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800568 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800569 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800570 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
571 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800572 *
573 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
574 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800575 * @param callback GATT callback handler that will receive asynchronous callbacks.
576 * @return If true, the callback will be called to notify success or failure,
577 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800578 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800579 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 if (DBG) Log.d(TAG, "registerApp()");
581 if (mService == null) return false;
582
583 mCallback = callback;
584 UUID uuid = UUID.randomUUID();
585 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
586
587 try {
588 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
589 } catch (RemoteException e) {
590 Log.e(TAG,"",e);
591 return false;
592 }
593
594 return true;
595 }
596
597 /**
598 * Unregister the current application and callbacks.
599 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800600 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800601 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
602 if (mService == null || mClientIf == 0) return;
603
604 try {
605 mCallback = null;
606 mService.unregisterClient(mClientIf);
607 mClientIf = 0;
608 } catch (RemoteException e) {
609 Log.e(TAG,"",e);
610 }
611 }
612
613 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800614 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800615 *
616 * <p>The connection may not be established right away, but will be
617 * completed when the remote device is available. A
618 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
619 * invoked when the connection state changes as a result of this function.
620 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700621 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800622 * the remote device, or rather passively scan and finalize the connection
623 * when the remote device is in range/available. Generally, the first ever
624 * connection to a device should be direct (autoConnect set to false) and
625 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800626 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800627 *
628 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
629 *
630 * @param device Remote device to connect to
631 * @param autoConnect Whether to directly connect to the remote device (false)
632 * or to automatically connect as soon as the remote
633 * device becomes available (true).
634 * @return true, if the connection attempt was initiated successfully
635 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800636 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
637 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
638 synchronized(mStateLock) {
639 if (mConnState != CONN_STATE_IDLE) {
640 throw new IllegalStateException("Not idle");
641 }
642 mConnState = CONN_STATE_CONNECTING;
643 }
Sungki Kim636ab032016-05-19 10:18:07 -0700644
645 mAutoConnect = autoConnect;
646
Matthew Xieddf7e472013-03-01 18:41:02 -0800647 if (!registerApp(callback)) {
648 synchronized(mStateLock) {
649 mConnState = CONN_STATE_IDLE;
650 }
651 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800652 return false;
653 }
654
Sungki Kim636ab032016-05-19 10:18:07 -0700655 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800656 return true;
657 }
658
659 /**
660 * Disconnects an established connection, or cancels a connection attempt
661 * currently in progress.
662 *
663 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800664 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800665 public void disconnect() {
666 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800667 if (mService == null || mClientIf == 0) return;
668
669 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800670 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800671 } catch (RemoteException e) {
672 Log.e(TAG,"",e);
673 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700674 }
675
676 /**
677 * Connect back to remote device.
678 *
679 * <p>This method is used to re-connect to a remote device after the
680 * connection has been dropped. If the device is not in range, the
681 * re-connection will be triggered once the device is back in range.
682 *
683 * @return true, if the connection attempt was initiated successfully
684 */
685 public boolean connect() {
686 try {
687 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700688 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700689 return true;
690 } catch (RemoteException e) {
691 Log.e(TAG,"",e);
692 return false;
693 }
694 }
695
696 /**
697 * Return the remote bluetooth device this GATT client targets to
698 *
699 * @return remote bluetooth device
700 */
701 public BluetoothDevice getDevice() {
702 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800703 }
704
705 /**
706 * Discovers services offered by a remote device as well as their
707 * characteristics and descriptors.
708 *
709 * <p>This is an asynchronous operation. Once service discovery is completed,
710 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
711 * triggered. If the discovery was successful, the remote services can be
712 * retrieved using the {@link #getServices} function.
713 *
714 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
715 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800716 * @return true, if the remote service discovery has been started
717 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800718 public boolean discoverServices() {
719 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800720 if (mService == null || mClientIf == 0) return false;
721
722 mServices.clear();
723
724 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800725 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800726 } catch (RemoteException e) {
727 Log.e(TAG,"",e);
728 return false;
729 }
730
731 return true;
732 }
733
734 /**
735 * Returns a list of GATT services offered by the remote device.
736 *
737 * <p>This function requires that service discovery has been completed
738 * for the given device.
739 *
740 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
741 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800742 * @return List of services on the remote device. Returns an empty list
743 * if service discovery has not yet been performed.
744 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800745 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800746 List<BluetoothGattService> result =
747 new ArrayList<BluetoothGattService>();
748
749 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800750 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800751 result.add(service);
752 }
753 }
754
755 return result;
756 }
757
758 /**
759 * Returns a {@link BluetoothGattService}, if the requested UUID is
760 * supported by the remote device.
761 *
762 * <p>This function requires that service discovery has been completed
763 * for the given device.
764 *
765 * <p>If multiple instances of the same service (as identified by UUID)
766 * exist, the first instance of the service is returned.
767 *
768 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
769 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800770 * @param uuid UUID of the requested service
771 * @return BluetoothGattService if supported, or null if the requested
772 * service is not offered by the remote device.
773 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800774 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800775 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800776 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800777 service.getUuid().equals(uuid)) {
778 return service;
779 }
780 }
781
782 return null;
783 }
784
785 /**
786 * Reads the requested characteristic from the associated remote device.
787 *
788 * <p>This is an asynchronous operation. The result of the read operation
789 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
790 * callback.
791 *
792 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
793 *
794 * @param characteristic Characteristic to read from the remote device
795 * @return true, if the read operation was initiated successfully
796 */
797 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
798 if ((characteristic.getProperties() &
799 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
800
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700801 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800802 if (mService == null || mClientIf == 0) return false;
803
804 BluetoothGattService service = characteristic.getService();
805 if (service == null) return false;
806
807 BluetoothDevice device = service.getDevice();
808 if (device == null) return false;
809
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700810 synchronized(mDeviceBusy) {
811 if (mDeviceBusy) return false;
812 mDeviceBusy = true;
813 }
814
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800815 try {
816 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700817 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800818 } catch (RemoteException e) {
819 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700820 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800821 return false;
822 }
823
824 return true;
825 }
826
827 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800828 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800829 *
830 * <p>Once the write operation has been completed, the
831 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
832 * reporting the result of the operation.
833 *
834 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
835 *
836 * @param characteristic Characteristic to write on the remote device
837 * @return true, if the write operation was initiated successfully
838 */
839 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
840 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
841 && (characteristic.getProperties() &
842 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
843
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700844 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800845 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800846
847 BluetoothGattService service = characteristic.getService();
848 if (service == null) return false;
849
850 BluetoothDevice device = service.getDevice();
851 if (device == null) return false;
852
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700853 synchronized(mDeviceBusy) {
854 if (mDeviceBusy) return false;
855 mDeviceBusy = true;
856 }
857
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800858 try {
859 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700860 characteristic.getInstanceId(), characteristic.getWriteType(),
861 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800862 } catch (RemoteException e) {
863 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700864 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800865 return false;
866 }
867
868 return true;
869 }
870
871 /**
872 * Reads the value for a given descriptor from the associated remote device.
873 *
874 * <p>Once the read operation has been completed, the
875 * {@link BluetoothGattCallback#onDescriptorRead} callback is
876 * triggered, signaling the result of the operation.
877 *
878 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
879 *
880 * @param descriptor Descriptor value to read from the remote device
881 * @return true, if the read operation was initiated successfully
882 */
883 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700884 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800885 if (mService == null || mClientIf == 0) return false;
886
887 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
888 if (characteristic == null) return false;
889
890 BluetoothGattService service = characteristic.getService();
891 if (service == null) return false;
892
893 BluetoothDevice device = service.getDevice();
894 if (device == null) return false;
895
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700896 synchronized(mDeviceBusy) {
897 if (mDeviceBusy) return false;
898 mDeviceBusy = true;
899 }
900
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800901 try {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700902 mService.readDescriptor(mClientIf, device.getAddress(),
903 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800904 } catch (RemoteException e) {
905 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700906 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800907 return false;
908 }
909
910 return true;
911 }
912
913 /**
914 * Write the value of a given descriptor to the associated remote device.
915 *
916 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
917 * triggered to report the result of the write operation.
918 *
919 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
920 *
921 * @param descriptor Descriptor to write to the associated remote device
922 * @return true, if the write operation was initiated successfully
923 */
924 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700925 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800926 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800927
928 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
929 if (characteristic == null) return false;
930
931 BluetoothGattService service = characteristic.getService();
932 if (service == null) return false;
933
934 BluetoothDevice device = service.getDevice();
935 if (device == null) return false;
936
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700937 synchronized(mDeviceBusy) {
938 if (mDeviceBusy) return false;
939 mDeviceBusy = true;
940 }
941
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800942 try {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700943 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jakub Pawlowski8e970d62016-03-30 22:58:17 -0700944 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800945 } catch (RemoteException e) {
946 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700947 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800948 return false;
949 }
950
951 return true;
952 }
953
954 /**
955 * Initiates a reliable write transaction for a given remote device.
956 *
957 * <p>Once a reliable write transaction has been initiated, all calls
958 * to {@link #writeCharacteristic} are sent to the remote device for
959 * verification and queued up for atomic execution. The application will
960 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
961 * in response to every {@link #writeCharacteristic} call and is responsible
962 * for verifying if the value has been transmitted accurately.
963 *
964 * <p>After all characteristics have been queued up and verified,
965 * {@link #executeReliableWrite} will execute all writes. If a characteristic
966 * was not written correctly, calling {@link #abortReliableWrite} will
967 * cancel the current transaction without commiting any values on the
968 * remote device.
969 *
970 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
971 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800972 * @return true, if the reliable write transaction has been initiated
973 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800974 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700975 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800976 if (mService == null || mClientIf == 0) return false;
977
978 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800979 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800980 } catch (RemoteException e) {
981 Log.e(TAG,"",e);
982 return false;
983 }
984
985 return true;
986 }
987
988 /**
989 * Executes a reliable write transaction for a given remote device.
990 *
991 * <p>This function will commit all queued up characteristic write
992 * operations for a given remote device.
993 *
994 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
995 * invoked to indicate whether the transaction has been executed correctly.
996 *
997 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
998 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800999 * @return true, if the request to execute the transaction has been sent
1000 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001001 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001002 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001003 if (mService == null || mClientIf == 0) return false;
1004
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001005 synchronized(mDeviceBusy) {
1006 if (mDeviceBusy) return false;
1007 mDeviceBusy = true;
1008 }
1009
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001010 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001011 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001012 } catch (RemoteException e) {
1013 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001014 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 return false;
1016 }
1017
1018 return true;
1019 }
1020
1021 /**
1022 * Cancels a reliable write transaction for a given device.
1023 *
1024 * <p>Calling this function will discard all queued characteristic write
1025 * operations for a given remote device.
1026 *
1027 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001028 */
John Du48f8b5d2013-08-19 12:20:37 -07001029 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001030 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001031 if (mService == null || mClientIf == 0) return;
1032
1033 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001034 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001035 } catch (RemoteException e) {
1036 Log.e(TAG,"",e);
1037 }
1038 }
1039
1040 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001041 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001042 */
1043 public void abortReliableWrite(BluetoothDevice mDevice) {
1044 abortReliableWrite();
1045 }
1046
1047 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001048 * Enable or disable notifications/indications for a given characteristic.
1049 *
1050 * <p>Once notifications are enabled for a characteristic, a
1051 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1052 * triggered if the remote device indicates that the given characteristic
1053 * has changed.
1054 *
1055 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1056 *
1057 * @param characteristic The characteristic for which to enable notifications
1058 * @param enable Set to true to enable notifications/indications
1059 * @return true, if the requested notification status was set successfully
1060 */
1061 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1062 boolean enable) {
1063 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1064 + " enable: " + enable);
1065 if (mService == null || mClientIf == 0) return false;
1066
1067 BluetoothGattService service = characteristic.getService();
1068 if (service == null) return false;
1069
1070 BluetoothDevice device = service.getDevice();
1071 if (device == null) return false;
1072
1073 try {
1074 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -07001075 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001076 } catch (RemoteException e) {
1077 Log.e(TAG,"",e);
1078 return false;
1079 }
1080
1081 return true;
1082 }
1083
1084 /**
1085 * Clears the internal cache and forces a refresh of the services from the
1086 * remote device.
1087 * @hide
1088 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001089 public boolean refresh() {
1090 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001091 if (mService == null || mClientIf == 0) return false;
1092
1093 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001094 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001095 } catch (RemoteException e) {
1096 Log.e(TAG,"",e);
1097 return false;
1098 }
1099
1100 return true;
1101 }
1102
1103 /**
1104 * Read the RSSI for a connected remote device.
1105 *
1106 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1107 * invoked when the RSSI value has been read.
1108 *
1109 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1110 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001111 * @return true, if the RSSI value has been requested successfully
1112 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001113 public boolean readRemoteRssi() {
1114 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001115 if (mService == null || mClientIf == 0) return false;
1116
1117 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001118 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001119 } catch (RemoteException e) {
1120 Log.e(TAG,"",e);
1121 return false;
1122 }
1123
1124 return true;
1125 }
1126
1127 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001128 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001129 *
1130 * <p>When performing a write request operation (write without response),
1131 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001132 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001133 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001134 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001135 * whether this operation was successful.
1136 *
1137 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1138 *
1139 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001140 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001141 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001142 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1143 + " mtu: " + mtu);
1144 if (mService == null || mClientIf == 0) return false;
1145
1146 try {
1147 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1148 } catch (RemoteException e) {
1149 Log.e(TAG,"",e);
1150 return false;
1151 }
1152
1153 return true;
1154 }
1155
1156 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001157 * Request a connection parameter update.
1158 *
1159 * <p>This function will send a connection parameter update request to the
1160 * remote device.
1161 *
1162 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001163 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1164 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1165 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001166 * @throws IllegalArgumentException If the parameters are outside of their
1167 * specified range.
1168 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001169 public boolean requestConnectionPriority(int connectionPriority) {
1170 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1171 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001172 throw new IllegalArgumentException("connectionPriority not within valid range");
1173 }
1174
Andre Eisenbach4072da02014-08-19 17:58:55 -07001175 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001176 if (mService == null || mClientIf == 0) return false;
1177
1178 try {
1179 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1180 } catch (RemoteException e) {
1181 Log.e(TAG,"",e);
1182 return false;
1183 }
1184
1185 return true;
1186 }
1187
1188 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001189 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1190 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001191 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001192 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001193 */
1194 @Override
1195 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001196 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001197 }
1198
1199 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001200 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1201 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001202 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001203 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001204 */
1205 @Override
1206 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001207 throw new UnsupportedOperationException
1208 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001209 }
1210
1211 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001212 * Not supported - please use
1213 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1214 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001215 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001216 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001217 */
1218 @Override
1219 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001220 throw new UnsupportedOperationException
1221 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001222 }
1223}