blob: 0eca4d670d3d6fd8c2682ef684cd996cbce8786d [file] [log] [blame]
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080019import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080020import android.os.ParcelUuid;
21import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.util.Log;
23
24import java.util.ArrayList;
25import java.util.List;
26import java.util.UUID;
27
28/**
Matthew Xieddf7e472013-03-01 18:41:02 -080029 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080030 *
Matthew Xieddf7e472013-03-01 18:41:02 -080031 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080032 * with Bluetooth Smart or Smart Ready devices.
33 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080034 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070035 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080036 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
37 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080038 */
39public final class BluetoothGatt implements BluetoothProfile {
40 private static final String TAG = "BluetoothGatt";
41 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070042 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080043
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044 private IBluetoothGatt mService;
45 private BluetoothGattCallback mCallback;
46 private int mClientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -080047 private BluetoothDevice mDevice;
48 private boolean mAutoConnect;
Jacky Cheung84c6d332016-07-29 11:49:27 -070049 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080050 private int mConnState;
51 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070052 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070053 private int mTransport;
Matthew Xieddf7e472013-03-01 18:41:02 -080054
Jacky Cheung84c6d332016-07-29 11:49:27 -070055 private static final int AUTH_RETRY_STATE_IDLE = 0;
56 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
57 private static final int AUTH_RETRY_STATE_MITM = 2;
58
Matthew Xieddf7e472013-03-01 18:41:02 -080059 private static final int CONN_STATE_IDLE = 0;
60 private static final int CONN_STATE_CONNECTING = 1;
61 private static final int CONN_STATE_CONNECTED = 2;
62 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070063 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080064
65 private List<BluetoothGattService> mServices;
66
Matthew Xieddf7e472013-03-01 18:41:02 -080067 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080068 public static final int GATT_SUCCESS = 0;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_READ_NOT_PERMITTED = 0x2;
72
Matthew Xieddf7e472013-03-01 18:41:02 -080073 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080074 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
75
76 /** Insufficient authentication for a given operation */
77 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
78
79 /** The given request is not supported */
80 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
81
82 /** Insufficient encryption for a given operation */
83 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
84
85 /** A read or write operation was requested with an invalid offset */
86 public static final int GATT_INVALID_OFFSET = 0x7;
87
88 /** A write operation exceeds the maximum length of the attribute */
89 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
90
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070091 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070092 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
93
Matthew Xie90ca8072013-05-28 21:06:50 +000094 /** A GATT operation failed, errors other than the above */
95 public static final int GATT_FAILURE = 0x101;
96
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080097 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070098 * Connection paramter update - Use the connection paramters recommended by the
99 * Bluetooth SIG. This is the default value if no connection parameter update
100 * is requested.
101 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700102 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700103
104 /**
105 * Connection paramter update - Request a high priority, low latency connection.
106 * An application should only request high priority connection paramters to transfer
107 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700108 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700109 * to reduce energy use.
110 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700111 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112
113 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700114 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700115
116 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800117 * No authentication required.
118 * @hide
119 */
120 /*package*/ static final int AUTHENTICATION_NONE = 0;
121
122 /**
123 * Authentication requested; no man-in-the-middle protection required.
124 * @hide
125 */
126 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
127
128 /**
129 * Authentication with man-in-the-middle protection requested.
130 * @hide
131 */
132 /*package*/ static final int AUTHENTICATION_MITM = 2;
133
134 /**
Wei Wange0d4afb2014-07-29 21:34:25 -0700135 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800136 */
137 private final IBluetoothGattCallback mBluetoothGattCallback =
Wei Wange0d4afb2014-07-29 21:34:25 -0700138 new BluetoothGattCallbackWrapper() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800139 /**
140 * Application interface registered - app is ready to go
141 * @hide
142 */
143 public void onClientRegistered(int status, int clientIf) {
144 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
145 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800146 if (VDBG) {
147 synchronized(mStateLock) {
148 if (mConnState != CONN_STATE_CONNECTING) {
149 Log.e(TAG, "Bad connection state: " + mConnState);
150 }
151 }
152 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800153 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800154 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700155 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800156 BluetoothProfile.STATE_DISCONNECTED);
157 synchronized(mStateLock) {
158 mConnState = CONN_STATE_IDLE;
159 }
160 return;
161 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800162 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800163 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700164 !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800165 } catch (RemoteException e) {
166 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800167 }
168 }
169
170 /**
171 * Client connection state changed
172 * @hide
173 */
174 public void onClientConnectionState(int status, int clientIf,
175 boolean connected, String address) {
176 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
177 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800178 if (!address.equals(mDevice.getAddress())) {
179 return;
180 }
181 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
182 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800183 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700184 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800185 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700186 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800187 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800188
189 synchronized(mStateLock) {
190 if (connected) {
191 mConnState = CONN_STATE_CONNECTED;
192 } else {
193 mConnState = CONN_STATE_IDLE;
194 }
195 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700196
197 synchronized(mDeviceBusy) {
198 mDeviceBusy = false;
199 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800200 }
201
202 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800203 * Remote search has been completed.
204 * The internal object structure should now reflect the state
205 * of the remote device database. Let the application know that
206 * we are done at this point.
207 * @hide
208 */
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800209 public void onSearchComplete(String address, List<BluetoothGattService> services,
210 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800211 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800212 if (!address.equals(mDevice.getAddress())) {
213 return;
214 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800215
216 for (BluetoothGattService s : services) {
217 //services we receive don't have device set properly.
218 s.setDevice(mDevice);
219 }
220
221 mServices.addAll(services);
222
223 // Fix references to included services, as they doesn't point to right objects.
224 for (BluetoothGattService fixedService : mServices) {
225 ArrayList<BluetoothGattService> includedServices =
226 new ArrayList(fixedService.getIncludedServices());
227 fixedService.getIncludedServices().clear();
228
229 for(BluetoothGattService brokenRef : includedServices) {
230 BluetoothGattService includedService = getService(mDevice,
231 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
232 if (includedService != null) {
233 fixedService.addIncludedService(includedService);
234 } else {
235 Log.e(TAG, "Broken GATT database: can't find included service.");
236 }
237 }
238 }
239
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800240 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700241 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800242 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700243 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800244 }
245 }
246
247 /**
248 * Remote characteristic has been read.
249 * Updates the internal value.
250 * @hide
251 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700252 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700253 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700254 + " handle=" + handle + " Status=" + status);
255
256 Log.w(TAG, "onCharacteristicRead() - Device=" + address
257 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800258
Matthew Xieddf7e472013-03-01 18:41:02 -0800259 if (!address.equals(mDevice.getAddress())) {
260 return;
261 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700262
263 synchronized(mDeviceBusy) {
264 mDeviceBusy = false;
265 }
266
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800267 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
268 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung84c6d332016-07-29 11:49:27 -0700269 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800270 try {
Jacky Cheung84c6d332016-07-29 11:49:27 -0700271 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
272 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
273 mService.readCharacteristic(mClientIf, address, handle, authReq);
274 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800275 return;
276 } catch (RemoteException e) {
277 Log.e(TAG,"",e);
278 }
279 }
280
Jacky Cheung84c6d332016-07-29 11:49:27 -0700281 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800282
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700283 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
284 if (characteristic == null) {
285 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
286 return;
287 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800288
289 if (status == 0) characteristic.setValue(value);
290
291 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700292 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800293 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700294 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800295 }
296 }
297
298 /**
299 * Characteristic has been written to the remote device.
300 * Let the app know how we did...
301 * @hide
302 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700303 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700304 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700305 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306
Matthew Xieddf7e472013-03-01 18:41:02 -0800307 if (!address.equals(mDevice.getAddress())) {
308 return;
309 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700310
311 synchronized(mDeviceBusy) {
312 mDeviceBusy = false;
313 }
314
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700315 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 if (characteristic == null) return;
317
318 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
319 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung84c6d332016-07-29 11:49:27 -0700320 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800321 try {
Jacky Cheung84c6d332016-07-29 11:49:27 -0700322 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
323 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700324 mService.writeCharacteristic(mClientIf, address, handle,
Jacky Cheung84c6d332016-07-29 11:49:27 -0700325 characteristic.getWriteType(), authReq, characteristic.getValue());
326 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800327 return;
328 } catch (RemoteException e) {
329 Log.e(TAG,"",e);
330 }
331 }
332
Jacky Cheung84c6d332016-07-29 11:49:27 -0700333 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800334
335 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700336 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800337 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700338 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800339 }
340 }
341
342 /**
343 * Remote characteristic has been updated.
344 * Updates the internal value.
345 * @hide
346 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700347 public void onNotify(String address, int handle, byte[] value) {
348 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800349
Matthew Xieddf7e472013-03-01 18:41:02 -0800350 if (!address.equals(mDevice.getAddress())) {
351 return;
352 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800353
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700354 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800355 if (characteristic == null) return;
356
357 characteristic.setValue(value);
358
359 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700360 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800361 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700362 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800363 }
364 }
365
366 /**
367 * Descriptor has been read.
368 * @hide
369 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700370 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
371 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800372
Matthew Xieddf7e472013-03-01 18:41:02 -0800373 if (!address.equals(mDevice.getAddress())) {
374 return;
375 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700376
377 synchronized(mDeviceBusy) {
378 mDeviceBusy = false;
379 }
380
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700381 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800382 if (descriptor == null) return;
383
384 if (status == 0) descriptor.setValue(value);
385
386 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
387 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung84c6d332016-07-29 11:49:27 -0700388 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800389 try {
Jacky Cheung84c6d332016-07-29 11:49:27 -0700390 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
391 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
392 mService.readDescriptor(mClientIf, address, handle, authReq);
393 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700394 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800395 } catch (RemoteException e) {
396 Log.e(TAG,"",e);
397 }
398 }
399
Jacky Cheung84c6d332016-07-29 11:49:27 -0700400 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800401
402 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700403 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800404 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700405 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800406 }
407 }
408
409 /**
410 * Descriptor write operation complete.
411 * @hide
412 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700413 public void onDescriptorWrite(String address, int status, int handle) {
414 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800415
Matthew Xieddf7e472013-03-01 18:41:02 -0800416 if (!address.equals(mDevice.getAddress())) {
417 return;
418 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700419
420 synchronized(mDeviceBusy) {
421 mDeviceBusy = false;
422 }
423
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700424 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800425 if (descriptor == null) return;
426
427 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
428 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung84c6d332016-07-29 11:49:27 -0700429 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800430 try {
Jacky Cheung84c6d332016-07-29 11:49:27 -0700431 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
432 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700433 mService.writeDescriptor(mClientIf, address, handle,
Andre Eisenbach613f4302016-03-29 20:52:38 -0700434 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
Jacky Cheung84c6d332016-07-29 11:49:27 -0700435 authReq, descriptor.getValue());
436 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700437 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800438 } catch (RemoteException e) {
439 Log.e(TAG,"",e);
440 }
441 }
442
Jacky Cheung84c6d332016-07-29 11:49:27 -0700443 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800444
445 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700446 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800447 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700448 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800449 }
450 }
451
452 /**
453 * Prepared write transaction completed (or aborted)
454 * @hide
455 */
456 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700457 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800458 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800459 if (!address.equals(mDevice.getAddress())) {
460 return;
461 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700462
463 synchronized(mDeviceBusy) {
464 mDeviceBusy = false;
465 }
466
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800467 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700468 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800469 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700470 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800471 }
472 }
473
474 /**
475 * Remote device RSSI has been read
476 * @hide
477 */
478 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700479 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800480 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800481 if (!address.equals(mDevice.getAddress())) {
482 return;
483 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800484 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700485 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800486 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700487 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800488 }
489 }
Wei Wangf3055892014-03-11 22:22:41 -0700490
491 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700492 * Callback invoked when the MTU for a given connection changes
493 * @hide
494 */
495 public void onConfigureMTU(String address, int mtu, int status) {
496 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
497 " mtu=" + mtu + " status=" + status);
498 if (!address.equals(mDevice.getAddress())) {
499 return;
500 }
501 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700502 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700503 } catch (Exception ex) {
504 Log.w(TAG, "Unhandled exception in callback", ex);
505 }
Wei Wangf3055892014-03-11 22:22:41 -0700506 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800507 };
508
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700509 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700510 int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800511 mService = iGatt;
512 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700513 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800514 mServices = new ArrayList<BluetoothGattService>();
515
Matthew Xieddf7e472013-03-01 18:41:02 -0800516 mConnState = CONN_STATE_IDLE;
Jacky Cheung84c6d332016-07-29 11:49:27 -0700517 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800518 }
519
520 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700521 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700522 *
523 * Application should call this method as early as possible after it is done with
524 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800525 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700526 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800527 if (DBG) Log.d(TAG, "close()");
528
529 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700530 mConnState = CONN_STATE_CLOSED;
Jacky Cheung84c6d332016-07-29 11:49:27 -0700531 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800532 }
533
534 /**
535 * Returns a service by UUID, instance and type.
536 * @hide
537 */
538 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
539 int instanceId, int type) {
540 for(BluetoothGattService svc : mServices) {
541 if (svc.getDevice().equals(device) &&
542 svc.getType() == type &&
543 svc.getInstanceId() == instanceId &&
544 svc.getUuid().equals(uuid)) {
545 return svc;
546 }
547 }
548 return null;
549 }
550
551
552 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700553 * Returns a characteristic with id equal to instanceId.
554 * @hide
555 */
556 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
557 for(BluetoothGattService svc : mServices) {
558 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700559 if (charac.getInstanceId() == instanceId)
560 return charac;
561 }
562 }
563 return null;
564 }
565
566 /**
567 * Returns a descriptor with id equal to instanceId.
568 * @hide
569 */
570 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
571 for(BluetoothGattService svc : mServices) {
572 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
573 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
574 if (desc.getInstanceId() == instanceId)
575 return desc;
576 }
577 }
578 }
579 return null;
580 }
581
582 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800583 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800584 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800585 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
586 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800587 *
588 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
589 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800590 * @param callback GATT callback handler that will receive asynchronous callbacks.
591 * @return If true, the callback will be called to notify success or failure,
592 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800593 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800594 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800595 if (DBG) Log.d(TAG, "registerApp()");
596 if (mService == null) return false;
597
598 mCallback = callback;
599 UUID uuid = UUID.randomUUID();
600 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
601
602 try {
603 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
604 } catch (RemoteException e) {
605 Log.e(TAG,"",e);
606 return false;
607 }
608
609 return true;
610 }
611
612 /**
613 * Unregister the current application and callbacks.
614 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800615 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800616 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
617 if (mService == null || mClientIf == 0) return;
618
619 try {
620 mCallback = null;
621 mService.unregisterClient(mClientIf);
622 mClientIf = 0;
623 } catch (RemoteException e) {
624 Log.e(TAG,"",e);
625 }
626 }
627
628 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800629 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800630 *
631 * <p>The connection may not be established right away, but will be
632 * completed when the remote device is available. A
633 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
634 * invoked when the connection state changes as a result of this function.
635 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700636 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800637 * the remote device, or rather passively scan and finalize the connection
638 * when the remote device is in range/available. Generally, the first ever
639 * connection to a device should be direct (autoConnect set to false) and
640 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800641 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800642 *
643 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
644 *
645 * @param device Remote device to connect to
646 * @param autoConnect Whether to directly connect to the remote device (false)
647 * or to automatically connect as soon as the remote
648 * device becomes available (true).
649 * @return true, if the connection attempt was initiated successfully
650 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800651 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
652 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
653 synchronized(mStateLock) {
654 if (mConnState != CONN_STATE_IDLE) {
655 throw new IllegalStateException("Not idle");
656 }
657 mConnState = CONN_STATE_CONNECTING;
658 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700659
660 mAutoConnect = autoConnect;
661
Matthew Xieddf7e472013-03-01 18:41:02 -0800662 if (!registerApp(callback)) {
663 synchronized(mStateLock) {
664 mConnState = CONN_STATE_IDLE;
665 }
666 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800667 return false;
668 }
669
Sungki Kimd35167a2016-05-19 10:18:07 -0700670 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800671 return true;
672 }
673
674 /**
675 * Disconnects an established connection, or cancels a connection attempt
676 * currently in progress.
677 *
678 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800679 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800680 public void disconnect() {
681 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682 if (mService == null || mClientIf == 0) return;
683
684 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800685 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800686 } catch (RemoteException e) {
687 Log.e(TAG,"",e);
688 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700689 }
690
691 /**
692 * Connect back to remote device.
693 *
694 * <p>This method is used to re-connect to a remote device after the
695 * connection has been dropped. If the device is not in range, the
696 * re-connection will be triggered once the device is back in range.
697 *
698 * @return true, if the connection attempt was initiated successfully
699 */
700 public boolean connect() {
701 try {
702 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700703 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700704 return true;
705 } catch (RemoteException e) {
706 Log.e(TAG,"",e);
707 return false;
708 }
709 }
710
711 /**
712 * Return the remote bluetooth device this GATT client targets to
713 *
714 * @return remote bluetooth device
715 */
716 public BluetoothDevice getDevice() {
717 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800718 }
719
720 /**
721 * Discovers services offered by a remote device as well as their
722 * characteristics and descriptors.
723 *
724 * <p>This is an asynchronous operation. Once service discovery is completed,
725 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
726 * triggered. If the discovery was successful, the remote services can be
727 * retrieved using the {@link #getServices} function.
728 *
729 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
730 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800731 * @return true, if the remote service discovery has been started
732 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800733 public boolean discoverServices() {
734 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800735 if (mService == null || mClientIf == 0) return false;
736
737 mServices.clear();
738
739 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800740 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800741 } catch (RemoteException e) {
742 Log.e(TAG,"",e);
743 return false;
744 }
745
746 return true;
747 }
748
749 /**
750 * Returns a list of GATT services offered by the remote device.
751 *
752 * <p>This function requires that service discovery has been completed
753 * for the given device.
754 *
755 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
756 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800757 * @return List of services on the remote device. Returns an empty list
758 * if service discovery has not yet been performed.
759 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800760 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800761 List<BluetoothGattService> result =
762 new ArrayList<BluetoothGattService>();
763
764 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800765 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800766 result.add(service);
767 }
768 }
769
770 return result;
771 }
772
773 /**
774 * Returns a {@link BluetoothGattService}, if the requested UUID is
775 * supported by the remote device.
776 *
777 * <p>This function requires that service discovery has been completed
778 * for the given device.
779 *
780 * <p>If multiple instances of the same service (as identified by UUID)
781 * exist, the first instance of the service is returned.
782 *
783 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
784 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800785 * @param uuid UUID of the requested service
786 * @return BluetoothGattService if supported, or null if the requested
787 * service is not offered by the remote device.
788 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800789 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800791 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800792 service.getUuid().equals(uuid)) {
793 return service;
794 }
795 }
796
797 return null;
798 }
799
800 /**
801 * Reads the requested characteristic from the associated remote device.
802 *
803 * <p>This is an asynchronous operation. The result of the read operation
804 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
805 * callback.
806 *
807 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
808 *
809 * @param characteristic Characteristic to read from the remote device
810 * @return true, if the read operation was initiated successfully
811 */
812 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
813 if ((characteristic.getProperties() &
814 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
815
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700816 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800817 if (mService == null || mClientIf == 0) return false;
818
819 BluetoothGattService service = characteristic.getService();
820 if (service == null) return false;
821
822 BluetoothDevice device = service.getDevice();
823 if (device == null) return false;
824
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700825 synchronized(mDeviceBusy) {
826 if (mDeviceBusy) return false;
827 mDeviceBusy = true;
828 }
829
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800830 try {
831 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700832 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800833 } catch (RemoteException e) {
834 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700835 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800836 return false;
837 }
838
839 return true;
840 }
841
842 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800843 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800844 *
845 * <p>Once the write operation has been completed, the
846 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
847 * reporting the result of the operation.
848 *
849 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
850 *
851 * @param characteristic Characteristic to write on the remote device
852 * @return true, if the write operation was initiated successfully
853 */
854 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
855 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
856 && (characteristic.getProperties() &
857 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
858
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700859 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800860 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800861
862 BluetoothGattService service = characteristic.getService();
863 if (service == null) return false;
864
865 BluetoothDevice device = service.getDevice();
866 if (device == null) return false;
867
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700868 synchronized(mDeviceBusy) {
869 if (mDeviceBusy) return false;
870 mDeviceBusy = true;
871 }
872
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800873 try {
874 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700875 characteristic.getInstanceId(), characteristic.getWriteType(),
876 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800877 } catch (RemoteException e) {
878 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700879 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800880 return false;
881 }
882
883 return true;
884 }
885
886 /**
887 * Reads the value for a given descriptor from the associated remote device.
888 *
889 * <p>Once the read operation has been completed, the
890 * {@link BluetoothGattCallback#onDescriptorRead} callback is
891 * triggered, signaling the result of the operation.
892 *
893 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
894 *
895 * @param descriptor Descriptor value to read from the remote device
896 * @return true, if the read operation was initiated successfully
897 */
898 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700899 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800900 if (mService == null || mClientIf == 0) return false;
901
902 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
903 if (characteristic == null) return false;
904
905 BluetoothGattService service = characteristic.getService();
906 if (service == null) return false;
907
908 BluetoothDevice device = service.getDevice();
909 if (device == null) return false;
910
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700911 synchronized(mDeviceBusy) {
912 if (mDeviceBusy) return false;
913 mDeviceBusy = true;
914 }
915
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800916 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700917 mService.readDescriptor(mClientIf, device.getAddress(),
918 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800919 } catch (RemoteException e) {
920 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700921 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800922 return false;
923 }
924
925 return true;
926 }
927
928 /**
929 * Write the value of a given descriptor to the associated remote device.
930 *
931 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
932 * triggered to report the result of the write operation.
933 *
934 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
935 *
936 * @param descriptor Descriptor to write to the associated remote device
937 * @return true, if the write operation was initiated successfully
938 */
939 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700940 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800941 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800942
943 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
944 if (characteristic == null) return false;
945
946 BluetoothGattService service = characteristic.getService();
947 if (service == null) return false;
948
949 BluetoothDevice device = service.getDevice();
950 if (device == null) return false;
951
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700952 synchronized(mDeviceBusy) {
953 if (mDeviceBusy) return false;
954 mDeviceBusy = true;
955 }
956
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800957 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700958 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Andre Eisenbach613f4302016-03-29 20:52:38 -0700959 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE,
960 descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800961 } catch (RemoteException e) {
962 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700963 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800964 return false;
965 }
966
967 return true;
968 }
969
970 /**
971 * Initiates a reliable write transaction for a given remote device.
972 *
973 * <p>Once a reliable write transaction has been initiated, all calls
974 * to {@link #writeCharacteristic} are sent to the remote device for
975 * verification and queued up for atomic execution. The application will
976 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
977 * in response to every {@link #writeCharacteristic} call and is responsible
978 * for verifying if the value has been transmitted accurately.
979 *
980 * <p>After all characteristics have been queued up and verified,
981 * {@link #executeReliableWrite} will execute all writes. If a characteristic
982 * was not written correctly, calling {@link #abortReliableWrite} will
983 * cancel the current transaction without commiting any values on the
984 * remote device.
985 *
986 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
987 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800988 * @return true, if the reliable write transaction has been initiated
989 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800990 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700991 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800992 if (mService == null || mClientIf == 0) return false;
993
994 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800995 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800996 } catch (RemoteException e) {
997 Log.e(TAG,"",e);
998 return false;
999 }
1000
1001 return true;
1002 }
1003
1004 /**
1005 * Executes a reliable write transaction for a given remote device.
1006 *
1007 * <p>This function will commit all queued up characteristic write
1008 * operations for a given remote device.
1009 *
1010 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1011 * invoked to indicate whether the transaction has been executed correctly.
1012 *
1013 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1014 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001015 * @return true, if the request to execute the transaction has been sent
1016 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001017 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001018 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001019 if (mService == null || mClientIf == 0) return false;
1020
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001021 synchronized(mDeviceBusy) {
1022 if (mDeviceBusy) return false;
1023 mDeviceBusy = true;
1024 }
1025
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001026 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001027 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001028 } catch (RemoteException e) {
1029 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001030 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001031 return false;
1032 }
1033
1034 return true;
1035 }
1036
1037 /**
1038 * Cancels a reliable write transaction for a given device.
1039 *
1040 * <p>Calling this function will discard all queued characteristic write
1041 * operations for a given remote device.
1042 *
1043 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001044 */
John Du48f8b5d2013-08-19 12:20:37 -07001045 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001046 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001047 if (mService == null || mClientIf == 0) return;
1048
1049 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001050 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001051 } catch (RemoteException e) {
1052 Log.e(TAG,"",e);
1053 }
1054 }
1055
1056 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001057 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001058 */
1059 public void abortReliableWrite(BluetoothDevice mDevice) {
1060 abortReliableWrite();
1061 }
1062
1063 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001064 * Enable or disable notifications/indications for a given characteristic.
1065 *
1066 * <p>Once notifications are enabled for a characteristic, a
1067 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1068 * triggered if the remote device indicates that the given characteristic
1069 * has changed.
1070 *
1071 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1072 *
1073 * @param characteristic The characteristic for which to enable notifications
1074 * @param enable Set to true to enable notifications/indications
1075 * @return true, if the requested notification status was set successfully
1076 */
1077 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1078 boolean enable) {
1079 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1080 + " enable: " + enable);
1081 if (mService == null || mClientIf == 0) return false;
1082
1083 BluetoothGattService service = characteristic.getService();
1084 if (service == null) return false;
1085
1086 BluetoothDevice device = service.getDevice();
1087 if (device == null) return false;
1088
1089 try {
1090 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001091 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001092 } catch (RemoteException e) {
1093 Log.e(TAG,"",e);
1094 return false;
1095 }
1096
1097 return true;
1098 }
1099
1100 /**
1101 * Clears the internal cache and forces a refresh of the services from the
1102 * remote device.
1103 * @hide
1104 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001105 public boolean refresh() {
1106 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001107 if (mService == null || mClientIf == 0) return false;
1108
1109 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001110 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001111 } catch (RemoteException e) {
1112 Log.e(TAG,"",e);
1113 return false;
1114 }
1115
1116 return true;
1117 }
1118
1119 /**
1120 * Read the RSSI for a connected remote device.
1121 *
1122 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1123 * invoked when the RSSI value has been read.
1124 *
1125 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1126 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001127 * @return true, if the RSSI value has been requested successfully
1128 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001129 public boolean readRemoteRssi() {
1130 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001131 if (mService == null || mClientIf == 0) return false;
1132
1133 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001134 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001135 } catch (RemoteException e) {
1136 Log.e(TAG,"",e);
1137 return false;
1138 }
1139
1140 return true;
1141 }
1142
1143 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001144 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001145 *
1146 * <p>When performing a write request operation (write without response),
1147 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001148 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001149 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001150 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001151 * whether this operation was successful.
1152 *
1153 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1154 *
1155 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001156 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001157 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001158 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1159 + " mtu: " + mtu);
1160 if (mService == null || mClientIf == 0) return false;
1161
1162 try {
1163 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1164 } catch (RemoteException e) {
1165 Log.e(TAG,"",e);
1166 return false;
1167 }
1168
1169 return true;
1170 }
1171
1172 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001173 * Request a connection parameter update.
1174 *
1175 * <p>This function will send a connection parameter update request to the
1176 * remote device.
1177 *
1178 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001179 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1180 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1181 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001182 * @throws IllegalArgumentException If the parameters are outside of their
1183 * specified range.
1184 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001185 public boolean requestConnectionPriority(int connectionPriority) {
1186 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1187 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001188 throw new IllegalArgumentException("connectionPriority not within valid range");
1189 }
1190
Andre Eisenbach4072da02014-08-19 17:58:55 -07001191 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001192 if (mService == null || mClientIf == 0) return false;
1193
1194 try {
1195 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1196 } catch (RemoteException e) {
1197 Log.e(TAG,"",e);
1198 return false;
1199 }
1200
1201 return true;
1202 }
1203
1204 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001205 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1206 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001207 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001208 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001209 */
1210 @Override
1211 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001212 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001213 }
1214
1215 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001216 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1217 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001218 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001219 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001220 */
1221 @Override
1222 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001223 throw new UnsupportedOperationException
1224 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001225 }
1226
1227 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001228 * Not supported - please use
1229 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1230 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001231 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001232 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 */
1234 @Override
1235 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001236 throw new UnsupportedOperationException
1237 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001238 }
1239}