blob: 0fe2abf7140223f25b56da257a025f1914237007 [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;
Matthew Xieddf7e472013-03-01 18:41:02 -080048 private BluetoothDevice mDevice;
49 private boolean mAutoConnect;
Jacky Cheung3854e222016-10-20 13:55:21 -070050 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080051 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
Jacky Cheung3854e222016-10-20 13:55:21 -070056 private static final int AUTH_RETRY_STATE_IDLE = 0;
57 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
58 private static final int AUTH_RETRY_STATE_MITM = 2;
59
Matthew Xieddf7e472013-03-01 18:41:02 -080060 private static final int CONN_STATE_IDLE = 0;
61 private static final int CONN_STATE_CONNECTING = 1;
62 private static final int CONN_STATE_CONNECTED = 2;
63 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070064 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065
66 private List<BluetoothGattService> mServices;
67
Matthew Xieddf7e472013-03-01 18:41:02 -080068 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080069 public static final int GATT_SUCCESS = 0;
70
Matthew Xieddf7e472013-03-01 18:41:02 -080071 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080072 public static final int GATT_READ_NOT_PERMITTED = 0x2;
73
Matthew Xieddf7e472013-03-01 18:41:02 -080074 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080075 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
76
77 /** Insufficient authentication for a given operation */
78 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
79
80 /** The given request is not supported */
81 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
82
83 /** Insufficient encryption for a given operation */
84 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
85
86 /** A read or write operation was requested with an invalid offset */
87 public static final int GATT_INVALID_OFFSET = 0x7;
88
89 /** A write operation exceeds the maximum length of the attribute */
90 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
91
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070092 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070093 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
94
Matthew Xie90ca8072013-05-28 21:06:50 +000095 /** A GATT operation failed, errors other than the above */
96 public static final int GATT_FAILURE = 0x101;
97
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080098 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070099 * Connection paramter update - Use the connection paramters recommended by the
100 * Bluetooth SIG. This is the default value if no connection parameter update
101 * is requested.
102 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700103 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700104
105 /**
106 * Connection paramter update - Request a high priority, low latency connection.
107 * An application should only request high priority connection paramters to transfer
108 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700109 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700110 * to reduce energy use.
111 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700112 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700113
114 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700115 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700116
117 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800118 * No authentication required.
119 * @hide
120 */
121 /*package*/ static final int AUTHENTICATION_NONE = 0;
122
123 /**
124 * Authentication requested; no man-in-the-middle protection required.
125 * @hide
126 */
127 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
128
129 /**
130 * Authentication with man-in-the-middle protection requested.
131 * @hide
132 */
133 /*package*/ static final int AUTHENTICATION_MITM = 2;
134
135 /**
Wei Wange0d4afb2014-07-29 21:34:25 -0700136 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 */
138 private final IBluetoothGattCallback mBluetoothGattCallback =
Wei Wange0d4afb2014-07-29 21:34:25 -0700139 new BluetoothGattCallbackWrapper() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800140 /**
141 * Application interface registered - app is ready to go
142 * @hide
143 */
144 public void onClientRegistered(int status, int clientIf) {
145 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
146 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800147 if (VDBG) {
148 synchronized(mStateLock) {
149 if (mConnState != CONN_STATE_CONNECTING) {
150 Log.e(TAG, "Bad connection state: " + mConnState);
151 }
152 }
153 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800154 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800155 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700156 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800157 BluetoothProfile.STATE_DISCONNECTED);
158 synchronized(mStateLock) {
159 mConnState = CONN_STATE_IDLE;
160 }
161 return;
162 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800163 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800164 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700165 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800166 } catch (RemoteException e) {
167 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800168 }
169 }
170
171 /**
172 * Client connection state changed
173 * @hide
174 */
175 public void onClientConnectionState(int status, int clientIf,
176 boolean connected, String address) {
177 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
178 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800179 if (!address.equals(mDevice.getAddress())) {
180 return;
181 }
182 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
183 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800184 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700185 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800186 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700187 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800188 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800189
190 synchronized(mStateLock) {
191 if (connected) {
192 mConnState = CONN_STATE_CONNECTED;
193 } else {
194 mConnState = CONN_STATE_IDLE;
195 }
196 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700197
198 synchronized(mDeviceBusy) {
199 mDeviceBusy = false;
200 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800201 }
202
203 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800204 * Remote search has been completed.
205 * The internal object structure should now reflect the state
206 * of the remote device database. Let the application know that
207 * we are done at this point.
208 * @hide
209 */
Jakub Pawlowski8d312a82016-03-01 18:50:27 -0800210 public void onSearchComplete(String address, List<BluetoothGattService> services,
211 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800212 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800213 if (!address.equals(mDevice.getAddress())) {
214 return;
215 }
Jakub Pawlowski8d312a82016-03-01 18:50:27 -0800216
217 for (BluetoothGattService s : services) {
218 //services we receive don't have device set properly.
219 s.setDevice(mDevice);
220 }
221
222 mServices.addAll(services);
223
224 // Fix references to included services, as they doesn't point to right objects.
225 for (BluetoothGattService fixedService : mServices) {
226 ArrayList<BluetoothGattService> includedServices =
227 new ArrayList(fixedService.getIncludedServices());
228 fixedService.getIncludedServices().clear();
229
230 for(BluetoothGattService brokenRef : includedServices) {
231 BluetoothGattService includedService = getService(mDevice,
232 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
233 if (includedService != null) {
234 fixedService.addIncludedService(includedService);
235 } else {
236 Log.e(TAG, "Broken GATT database: can't find included service.");
237 }
238 }
239 }
240
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800241 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700242 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800243 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700244 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800245 }
246 }
247
248 /**
249 * Remote characteristic has been read.
250 * Updates the internal value.
251 * @hide
252 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700253 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700254 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700255 + " handle=" + handle + " Status=" + status);
256
Matthew Xieddf7e472013-03-01 18:41:02 -0800257 if (!address.equals(mDevice.getAddress())) {
258 return;
259 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700260
261 synchronized(mDeviceBusy) {
262 mDeviceBusy = false;
263 }
264
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800265 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
266 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700267 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800268 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700269 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
270 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
271 mService.readCharacteristic(mClientIf, address, handle, authReq);
272 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800273 return;
274 } catch (RemoteException e) {
275 Log.e(TAG,"",e);
276 }
277 }
278
Jacky Cheung3854e222016-10-20 13:55:21 -0700279 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800280
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700281 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
282 if (characteristic == null) {
283 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
284 return;
285 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800286
287 if (status == 0) characteristic.setValue(value);
288
289 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700290 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800291 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700292 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800293 }
294 }
295
296 /**
297 * Characteristic has been written to the remote device.
298 * Let the app know how we did...
299 * @hide
300 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700301 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700302 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700303 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800304
Matthew Xieddf7e472013-03-01 18:41:02 -0800305 if (!address.equals(mDevice.getAddress())) {
306 return;
307 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700308
309 synchronized(mDeviceBusy) {
310 mDeviceBusy = false;
311 }
312
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700313 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800314 if (characteristic == null) return;
315
316 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
317 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700318 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800319 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700320 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
321 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700322 mService.writeCharacteristic(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700323 characteristic.getWriteType(), authReq, characteristic.getValue());
324 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800325 return;
326 } catch (RemoteException e) {
327 Log.e(TAG,"",e);
328 }
329 }
330
Jacky Cheung3854e222016-10-20 13:55:21 -0700331 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800332
333 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700334 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800335 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700336 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800337 }
338 }
339
340 /**
341 * Remote characteristic has been updated.
342 * Updates the internal value.
343 * @hide
344 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700345 public void onNotify(String address, int handle, byte[] value) {
346 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800347
Matthew Xieddf7e472013-03-01 18:41:02 -0800348 if (!address.equals(mDevice.getAddress())) {
349 return;
350 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800351
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700352 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800353 if (characteristic == null) return;
354
355 characteristic.setValue(value);
356
357 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700358 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800359 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700360 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800361 }
362 }
363
364 /**
365 * Descriptor has been read.
366 * @hide
367 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700368 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
369 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800370
Matthew Xieddf7e472013-03-01 18:41:02 -0800371 if (!address.equals(mDevice.getAddress())) {
372 return;
373 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700374
375 synchronized(mDeviceBusy) {
376 mDeviceBusy = false;
377 }
378
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700379 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800380 if (descriptor == null) return;
381
382 if (status == 0) descriptor.setValue(value);
383
384 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
385 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700386 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800387 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700388 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
389 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
390 mService.readDescriptor(mClientIf, address, handle, authReq);
391 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700392 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800393 } catch (RemoteException e) {
394 Log.e(TAG,"",e);
395 }
396 }
397
Jacky Cheung3854e222016-10-20 13:55:21 -0700398 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800399
400 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700401 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800402 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700403 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800404 }
405 }
406
407 /**
408 * Descriptor write operation complete.
409 * @hide
410 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700411 public void onDescriptorWrite(String address, int status, int handle) {
412 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800413
Matthew Xieddf7e472013-03-01 18:41:02 -0800414 if (!address.equals(mDevice.getAddress())) {
415 return;
416 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700417
418 synchronized(mDeviceBusy) {
419 mDeviceBusy = false;
420 }
421
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700422 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800423 if (descriptor == null) return;
424
425 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
426 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700427 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800428 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700429 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
430 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700431 mService.writeDescriptor(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700432 authReq, descriptor.getValue());
433 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700434 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800435 } catch (RemoteException e) {
436 Log.e(TAG,"",e);
437 }
438 }
439
Jacky Cheung3854e222016-10-20 13:55:21 -0700440 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800441
442 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700443 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800444 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700445 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800446 }
447 }
448
449 /**
450 * Prepared write transaction completed (or aborted)
451 * @hide
452 */
453 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700454 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800455 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800456 if (!address.equals(mDevice.getAddress())) {
457 return;
458 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700459
460 synchronized(mDeviceBusy) {
461 mDeviceBusy = false;
462 }
463
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800464 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700465 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800466 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700467 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800468 }
469 }
470
471 /**
472 * Remote device RSSI has been read
473 * @hide
474 */
475 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700476 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800477 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800478 if (!address.equals(mDevice.getAddress())) {
479 return;
480 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800481 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700482 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800483 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700484 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800485 }
486 }
Wei Wangf3055892014-03-11 22:22:41 -0700487
488 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700489 * Callback invoked when the MTU for a given connection changes
490 * @hide
491 */
492 public void onConfigureMTU(String address, int mtu, int status) {
493 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
494 " mtu=" + mtu + " status=" + status);
495 if (!address.equals(mDevice.getAddress())) {
496 return;
497 }
498 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700499 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700500 } catch (Exception ex) {
501 Log.w(TAG, "Unhandled exception in callback", ex);
502 }
Wei Wangf3055892014-03-11 22:22:41 -0700503 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504 };
505
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700506 /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
507 int transport) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800508 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800509 mService = iGatt;
510 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700511 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800512 mServices = new ArrayList<BluetoothGattService>();
513
Matthew Xieddf7e472013-03-01 18:41:02 -0800514 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700515 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800516 }
517
518 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700519 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700520 *
521 * Application should call this method as early as possible after it is done with
522 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700524 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800525 if (DBG) Log.d(TAG, "close()");
526
527 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700528 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700529 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800530 }
531
532 /**
533 * Returns a service by UUID, instance and type.
534 * @hide
535 */
536 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
537 int instanceId, int type) {
538 for(BluetoothGattService svc : mServices) {
539 if (svc.getDevice().equals(device) &&
540 svc.getType() == type &&
541 svc.getInstanceId() == instanceId &&
542 svc.getUuid().equals(uuid)) {
543 return svc;
544 }
545 }
546 return null;
547 }
548
549
550 /**
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700551 * Returns a characteristic with id equal to instanceId.
552 * @hide
553 */
554 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
555 for(BluetoothGattService svc : mServices) {
556 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700557 if (charac.getInstanceId() == instanceId)
558 return charac;
559 }
560 }
561 return null;
562 }
563
564 /**
565 * Returns a descriptor with id equal to instanceId.
566 * @hide
567 */
568 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
569 for(BluetoothGattService svc : mServices) {
570 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
571 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
572 if (desc.getInstanceId() == instanceId)
573 return desc;
574 }
575 }
576 }
577 return null;
578 }
579
580 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800581 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800583 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
584 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800585 *
586 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
587 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800588 * @param callback GATT callback handler that will receive asynchronous callbacks.
589 * @return If true, the callback will be called to notify success or failure,
590 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800591 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800592 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800593 if (DBG) Log.d(TAG, "registerApp()");
594 if (mService == null) return false;
595
596 mCallback = callback;
597 UUID uuid = UUID.randomUUID();
598 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
599
600 try {
601 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
602 } catch (RemoteException e) {
603 Log.e(TAG,"",e);
604 return false;
605 }
606
607 return true;
608 }
609
610 /**
611 * Unregister the current application and callbacks.
612 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800613 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800614 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
615 if (mService == null || mClientIf == 0) return;
616
617 try {
618 mCallback = null;
619 mService.unregisterClient(mClientIf);
620 mClientIf = 0;
621 } catch (RemoteException e) {
622 Log.e(TAG,"",e);
623 }
624 }
625
626 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800627 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800628 *
629 * <p>The connection may not be established right away, but will be
630 * completed when the remote device is available. A
631 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
632 * invoked when the connection state changes as a result of this function.
633 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700634 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800635 * the remote device, or rather passively scan and finalize the connection
636 * when the remote device is in range/available. Generally, the first ever
637 * connection to a device should be direct (autoConnect set to false) and
638 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800639 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800640 *
641 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
642 *
643 * @param device Remote device to connect to
644 * @param autoConnect Whether to directly connect to the remote device (false)
645 * or to automatically connect as soon as the remote
646 * device becomes available (true).
647 * @return true, if the connection attempt was initiated successfully
648 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800649 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
650 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
651 synchronized(mStateLock) {
652 if (mConnState != CONN_STATE_IDLE) {
653 throw new IllegalStateException("Not idle");
654 }
655 mConnState = CONN_STATE_CONNECTING;
656 }
Sungki Kim636ab032016-05-19 10:18:07 -0700657
658 mAutoConnect = autoConnect;
659
Matthew Xieddf7e472013-03-01 18:41:02 -0800660 if (!registerApp(callback)) {
661 synchronized(mStateLock) {
662 mConnState = CONN_STATE_IDLE;
663 }
664 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800665 return false;
666 }
667
Sungki Kim636ab032016-05-19 10:18:07 -0700668 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800669 return true;
670 }
671
672 /**
673 * Disconnects an established connection, or cancels a connection attempt
674 * currently in progress.
675 *
676 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800677 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800678 public void disconnect() {
679 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800680 if (mService == null || mClientIf == 0) return;
681
682 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800683 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800684 } catch (RemoteException e) {
685 Log.e(TAG,"",e);
686 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700687 }
688
689 /**
690 * Connect back to remote device.
691 *
692 * <p>This method is used to re-connect to a remote device after the
693 * connection has been dropped. If the device is not in range, the
694 * re-connection will be triggered once the device is back in range.
695 *
696 * @return true, if the connection attempt was initiated successfully
697 */
698 public boolean connect() {
699 try {
700 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700701 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700702 return true;
703 } catch (RemoteException e) {
704 Log.e(TAG,"",e);
705 return false;
706 }
707 }
708
709 /**
710 * Return the remote bluetooth device this GATT client targets to
711 *
712 * @return remote bluetooth device
713 */
714 public BluetoothDevice getDevice() {
715 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800716 }
717
718 /**
719 * Discovers services offered by a remote device as well as their
720 * characteristics and descriptors.
721 *
722 * <p>This is an asynchronous operation. Once service discovery is completed,
723 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
724 * triggered. If the discovery was successful, the remote services can be
725 * retrieved using the {@link #getServices} function.
726 *
727 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
728 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800729 * @return true, if the remote service discovery has been started
730 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800731 public boolean discoverServices() {
732 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800733 if (mService == null || mClientIf == 0) return false;
734
735 mServices.clear();
736
737 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800738 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800739 } catch (RemoteException e) {
740 Log.e(TAG,"",e);
741 return false;
742 }
743
744 return true;
745 }
746
747 /**
748 * Returns a list of GATT services offered by the remote device.
749 *
750 * <p>This function requires that service discovery has been completed
751 * for the given device.
752 *
753 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
754 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800755 * @return List of services on the remote device. Returns an empty list
756 * if service discovery has not yet been performed.
757 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800758 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800759 List<BluetoothGattService> result =
760 new ArrayList<BluetoothGattService>();
761
762 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800763 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800764 result.add(service);
765 }
766 }
767
768 return result;
769 }
770
771 /**
772 * Returns a {@link BluetoothGattService}, if the requested UUID is
773 * supported by the remote device.
774 *
775 * <p>This function requires that service discovery has been completed
776 * for the given device.
777 *
778 * <p>If multiple instances of the same service (as identified by UUID)
779 * exist, the first instance of the service is returned.
780 *
781 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
782 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800783 * @param uuid UUID of the requested service
784 * @return BluetoothGattService if supported, or null if the requested
785 * service is not offered by the remote device.
786 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800787 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800788 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800789 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 service.getUuid().equals(uuid)) {
791 return service;
792 }
793 }
794
795 return null;
796 }
797
798 /**
799 * Reads the requested characteristic from the associated remote device.
800 *
801 * <p>This is an asynchronous operation. The result of the read operation
802 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
803 * callback.
804 *
805 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
806 *
807 * @param characteristic Characteristic to read from the remote device
808 * @return true, if the read operation was initiated successfully
809 */
810 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
811 if ((characteristic.getProperties() &
812 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
813
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700814 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800815 if (mService == null || mClientIf == 0) return false;
816
817 BluetoothGattService service = characteristic.getService();
818 if (service == null) return false;
819
820 BluetoothDevice device = service.getDevice();
821 if (device == null) return false;
822
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700823 synchronized(mDeviceBusy) {
824 if (mDeviceBusy) return false;
825 mDeviceBusy = true;
826 }
827
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800828 try {
829 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700830 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800831 } catch (RemoteException e) {
832 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700833 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800834 return false;
835 }
836
837 return true;
838 }
839
840 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800841 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800842 *
843 * <p>Once the write operation has been completed, the
844 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
845 * reporting the result of the operation.
846 *
847 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
848 *
849 * @param characteristic Characteristic to write on the remote device
850 * @return true, if the write operation was initiated successfully
851 */
852 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
853 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
854 && (characteristic.getProperties() &
855 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
856
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700857 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800858 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800859
860 BluetoothGattService service = characteristic.getService();
861 if (service == null) return false;
862
863 BluetoothDevice device = service.getDevice();
864 if (device == null) return false;
865
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700866 synchronized(mDeviceBusy) {
867 if (mDeviceBusy) return false;
868 mDeviceBusy = true;
869 }
870
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800871 try {
872 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700873 characteristic.getInstanceId(), characteristic.getWriteType(),
874 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800875 } catch (RemoteException e) {
876 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700877 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800878 return false;
879 }
880
881 return true;
882 }
883
884 /**
885 * Reads the value for a given descriptor from the associated remote device.
886 *
887 * <p>Once the read operation has been completed, the
888 * {@link BluetoothGattCallback#onDescriptorRead} callback is
889 * triggered, signaling the result of the operation.
890 *
891 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
892 *
893 * @param descriptor Descriptor value to read from the remote device
894 * @return true, if the read operation was initiated successfully
895 */
896 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700897 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800898 if (mService == null || mClientIf == 0) return false;
899
900 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
901 if (characteristic == null) return false;
902
903 BluetoothGattService service = characteristic.getService();
904 if (service == null) return false;
905
906 BluetoothDevice device = service.getDevice();
907 if (device == null) return false;
908
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700909 synchronized(mDeviceBusy) {
910 if (mDeviceBusy) return false;
911 mDeviceBusy = true;
912 }
913
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800914 try {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700915 mService.readDescriptor(mClientIf, device.getAddress(),
916 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800917 } catch (RemoteException e) {
918 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700919 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800920 return false;
921 }
922
923 return true;
924 }
925
926 /**
927 * Write the value of a given descriptor to the associated remote device.
928 *
929 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
930 * triggered to report the result of the write operation.
931 *
932 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
933 *
934 * @param descriptor Descriptor to write to the associated remote device
935 * @return true, if the write operation was initiated successfully
936 */
937 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700938 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800939 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800940
941 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
942 if (characteristic == null) return false;
943
944 BluetoothGattService service = characteristic.getService();
945 if (service == null) return false;
946
947 BluetoothDevice device = service.getDevice();
948 if (device == null) return false;
949
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700950 synchronized(mDeviceBusy) {
951 if (mDeviceBusy) return false;
952 mDeviceBusy = true;
953 }
954
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800955 try {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700956 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jakub Pawlowski8e970d62016-03-30 22:58:17 -0700957 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800958 } catch (RemoteException e) {
959 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700960 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800961 return false;
962 }
963
964 return true;
965 }
966
967 /**
968 * Initiates a reliable write transaction for a given remote device.
969 *
970 * <p>Once a reliable write transaction has been initiated, all calls
971 * to {@link #writeCharacteristic} are sent to the remote device for
972 * verification and queued up for atomic execution. The application will
973 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
974 * in response to every {@link #writeCharacteristic} call and is responsible
975 * for verifying if the value has been transmitted accurately.
976 *
977 * <p>After all characteristics have been queued up and verified,
978 * {@link #executeReliableWrite} will execute all writes. If a characteristic
979 * was not written correctly, calling {@link #abortReliableWrite} will
980 * cancel the current transaction without commiting any values on the
981 * remote device.
982 *
983 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
984 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800985 * @return true, if the reliable write transaction has been initiated
986 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800987 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700988 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800989 if (mService == null || mClientIf == 0) return false;
990
991 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800992 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800993 } catch (RemoteException e) {
994 Log.e(TAG,"",e);
995 return false;
996 }
997
998 return true;
999 }
1000
1001 /**
1002 * Executes a reliable write transaction for a given remote device.
1003 *
1004 * <p>This function will commit all queued up characteristic write
1005 * operations for a given remote device.
1006 *
1007 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1008 * invoked to indicate whether the transaction has been executed correctly.
1009 *
1010 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1011 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001012 * @return true, if the request to execute the transaction has been sent
1013 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001014 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001015 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001016 if (mService == null || mClientIf == 0) return false;
1017
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001018 synchronized(mDeviceBusy) {
1019 if (mDeviceBusy) return false;
1020 mDeviceBusy = true;
1021 }
1022
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001023 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001024 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001025 } catch (RemoteException e) {
1026 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001027 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001028 return false;
1029 }
1030
1031 return true;
1032 }
1033
1034 /**
1035 * Cancels a reliable write transaction for a given device.
1036 *
1037 * <p>Calling this function will discard all queued characteristic write
1038 * operations for a given remote device.
1039 *
1040 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001041 */
John Du48f8b5d2013-08-19 12:20:37 -07001042 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001043 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001044 if (mService == null || mClientIf == 0) return;
1045
1046 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001047 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001048 } catch (RemoteException e) {
1049 Log.e(TAG,"",e);
1050 }
1051 }
1052
1053 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001054 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001055 */
1056 public void abortReliableWrite(BluetoothDevice mDevice) {
1057 abortReliableWrite();
1058 }
1059
1060 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001061 * Enable or disable notifications/indications for a given characteristic.
1062 *
1063 * <p>Once notifications are enabled for a characteristic, a
1064 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1065 * triggered if the remote device indicates that the given characteristic
1066 * has changed.
1067 *
1068 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1069 *
1070 * @param characteristic The characteristic for which to enable notifications
1071 * @param enable Set to true to enable notifications/indications
1072 * @return true, if the requested notification status was set successfully
1073 */
1074 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1075 boolean enable) {
1076 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1077 + " enable: " + enable);
1078 if (mService == null || mClientIf == 0) return false;
1079
1080 BluetoothGattService service = characteristic.getService();
1081 if (service == null) return false;
1082
1083 BluetoothDevice device = service.getDevice();
1084 if (device == null) return false;
1085
1086 try {
1087 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -07001088 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001089 } catch (RemoteException e) {
1090 Log.e(TAG,"",e);
1091 return false;
1092 }
1093
1094 return true;
1095 }
1096
1097 /**
1098 * Clears the internal cache and forces a refresh of the services from the
1099 * remote device.
1100 * @hide
1101 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001102 public boolean refresh() {
1103 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001104 if (mService == null || mClientIf == 0) return false;
1105
1106 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001107 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001108 } catch (RemoteException e) {
1109 Log.e(TAG,"",e);
1110 return false;
1111 }
1112
1113 return true;
1114 }
1115
1116 /**
1117 * Read the RSSI for a connected remote device.
1118 *
1119 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1120 * invoked when the RSSI value has been read.
1121 *
1122 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1123 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001124 * @return true, if the RSSI value has been requested successfully
1125 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001126 public boolean readRemoteRssi() {
1127 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001128 if (mService == null || mClientIf == 0) return false;
1129
1130 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001131 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001132 } catch (RemoteException e) {
1133 Log.e(TAG,"",e);
1134 return false;
1135 }
1136
1137 return true;
1138 }
1139
1140 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001141 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001142 *
1143 * <p>When performing a write request operation (write without response),
1144 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001145 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001146 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001147 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001148 * whether this operation was successful.
1149 *
1150 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1151 *
1152 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001153 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001154 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001155 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1156 + " mtu: " + mtu);
1157 if (mService == null || mClientIf == 0) return false;
1158
1159 try {
1160 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1161 } catch (RemoteException e) {
1162 Log.e(TAG,"",e);
1163 return false;
1164 }
1165
1166 return true;
1167 }
1168
1169 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001170 * Request a connection parameter update.
1171 *
1172 * <p>This function will send a connection parameter update request to the
1173 * remote device.
1174 *
1175 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001176 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1177 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1178 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001179 * @throws IllegalArgumentException If the parameters are outside of their
1180 * specified range.
1181 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001182 public boolean requestConnectionPriority(int connectionPriority) {
1183 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1184 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001185 throw new IllegalArgumentException("connectionPriority not within valid range");
1186 }
1187
Andre Eisenbach4072da02014-08-19 17:58:55 -07001188 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001189 if (mService == null || mClientIf == 0) return false;
1190
1191 try {
1192 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1193 } catch (RemoteException e) {
1194 Log.e(TAG,"",e);
1195 return false;
1196 }
1197
1198 return true;
1199 }
1200
1201 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001202 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1203 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001204 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001205 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001206 */
1207 @Override
1208 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001209 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001210 }
1211
1212 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001213 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1214 * with {@link BluetoothProfile#GATT} as 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> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001220 throw new UnsupportedOperationException
1221 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001222 }
1223
1224 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001225 * Not supported - please use
1226 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1227 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001228 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001229 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001230 */
1231 @Override
1232 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001233 throw new UnsupportedOperationException
1234 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001235 }
1236}