blob: 97a32974d2ab4756b30b480bcb6ef8c89e47f8fe [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 Cheung3854e222016-10-20 13:55:21 -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 Cheung3854e222016-10-20 13:55:21 -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 =
Jakub Pawlowski1b49e6e2016-10-26 13:05:30 -0700138 new IBluetoothGattCallback.Stub() {
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 Pawlowski8d312a82016-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 Pawlowski8d312a82016-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 Pawlowskia9e27ec2016-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 Pawlowskia9e27ec2016-03-17 16:00:16 -0700254 + " handle=" + handle + " Status=" + status);
255
Matthew Xieddf7e472013-03-01 18:41:02 -0800256 if (!address.equals(mDevice.getAddress())) {
257 return;
258 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700259
260 synchronized(mDeviceBusy) {
261 mDeviceBusy = false;
262 }
263
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800264 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
265 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700266 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800267 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700268 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
269 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
270 mService.readCharacteristic(mClientIf, address, handle, authReq);
271 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800272 return;
273 } catch (RemoteException e) {
274 Log.e(TAG,"",e);
275 }
276 }
277
Jacky Cheung3854e222016-10-20 13:55:21 -0700278 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800279
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700280 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
281 if (characteristic == null) {
282 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
283 return;
284 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800285
286 if (status == 0) characteristic.setValue(value);
287
288 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700289 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800290 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700291 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800292 }
293 }
294
295 /**
296 * Characteristic has been written to the remote device.
297 * Let the app know how we did...
298 * @hide
299 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700300 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700301 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700302 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800303
Matthew Xieddf7e472013-03-01 18:41:02 -0800304 if (!address.equals(mDevice.getAddress())) {
305 return;
306 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700307
308 synchronized(mDeviceBusy) {
309 mDeviceBusy = false;
310 }
311
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700312 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800313 if (characteristic == null) return;
314
315 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
316 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700317 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800318 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700319 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
320 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700321 mService.writeCharacteristic(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700322 characteristic.getWriteType(), authReq, characteristic.getValue());
323 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800324 return;
325 } catch (RemoteException e) {
326 Log.e(TAG,"",e);
327 }
328 }
329
Jacky Cheung3854e222016-10-20 13:55:21 -0700330 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800331
332 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700333 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800334 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700335 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800336 }
337 }
338
339 /**
340 * Remote characteristic has been updated.
341 * Updates the internal value.
342 * @hide
343 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700344 public void onNotify(String address, int handle, byte[] value) {
345 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346
Matthew Xieddf7e472013-03-01 18:41:02 -0800347 if (!address.equals(mDevice.getAddress())) {
348 return;
349 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800350
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700351 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800352 if (characteristic == null) return;
353
354 characteristic.setValue(value);
355
356 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700357 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800358 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700359 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800360 }
361 }
362
363 /**
364 * Descriptor has been read.
365 * @hide
366 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700367 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
368 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800369
Matthew Xieddf7e472013-03-01 18:41:02 -0800370 if (!address.equals(mDevice.getAddress())) {
371 return;
372 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700373
374 synchronized(mDeviceBusy) {
375 mDeviceBusy = false;
376 }
377
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700378 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800379 if (descriptor == null) return;
380
381 if (status == 0) descriptor.setValue(value);
382
383 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
384 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700385 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800386 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700387 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
388 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
389 mService.readDescriptor(mClientIf, address, handle, authReq);
390 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700391 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800392 } catch (RemoteException e) {
393 Log.e(TAG,"",e);
394 }
395 }
396
Jacky Cheung3854e222016-10-20 13:55:21 -0700397 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800398
399 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700400 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800401 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700402 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800403 }
404 }
405
406 /**
407 * Descriptor write operation complete.
408 * @hide
409 */
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700410 public void onDescriptorWrite(String address, int status, int handle) {
411 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800412
Matthew Xieddf7e472013-03-01 18:41:02 -0800413 if (!address.equals(mDevice.getAddress())) {
414 return;
415 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700416
417 synchronized(mDeviceBusy) {
418 mDeviceBusy = false;
419 }
420
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700421 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800422 if (descriptor == null) return;
423
424 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
425 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700426 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800427 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700428 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
429 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700430 mService.writeDescriptor(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700431 authReq, descriptor.getValue());
432 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700433 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800434 } catch (RemoteException e) {
435 Log.e(TAG,"",e);
436 }
437 }
438
Jacky Cheung3854e222016-10-20 13:55:21 -0700439 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440
441 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700442 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800443 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700444 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800445 }
446 }
447
448 /**
449 * Prepared write transaction completed (or aborted)
450 * @hide
451 */
452 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700453 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800454 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800455 if (!address.equals(mDevice.getAddress())) {
456 return;
457 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700458
459 synchronized(mDeviceBusy) {
460 mDeviceBusy = false;
461 }
462
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800463 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700464 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800465 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700466 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800467 }
468 }
469
470 /**
471 * Remote device RSSI has been read
472 * @hide
473 */
474 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700475 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800476 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800477 if (!address.equals(mDevice.getAddress())) {
478 return;
479 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800480 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700481 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800482 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700483 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800484 }
485 }
Wei Wangf3055892014-03-11 22:22:41 -0700486
487 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700488 * Callback invoked when the MTU for a given connection changes
489 * @hide
490 */
491 public void onConfigureMTU(String address, int mtu, int status) {
492 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
493 " mtu=" + mtu + " status=" + status);
494 if (!address.equals(mDevice.getAddress())) {
495 return;
496 }
497 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700498 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700499 } catch (Exception ex) {
500 Log.w(TAG, "Unhandled exception in callback", ex);
501 }
Wei Wangf3055892014-03-11 22:22:41 -0700502 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800503 };
504
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700505 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700506 int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800507 mService = iGatt;
508 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700509 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800510 mServices = new ArrayList<BluetoothGattService>();
511
Matthew Xieddf7e472013-03-01 18:41:02 -0800512 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700513 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800514 }
515
516 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700517 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700518 *
519 * Application should call this method as early as possible after it is done with
520 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800521 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700522 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 if (DBG) Log.d(TAG, "close()");
524
525 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700526 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700527 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800528 }
529
530 /**
531 * Returns a service by UUID, instance and type.
532 * @hide
533 */
534 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
535 int instanceId, int type) {
536 for(BluetoothGattService svc : mServices) {
537 if (svc.getDevice().equals(device) &&
538 svc.getType() == type &&
539 svc.getInstanceId() == instanceId &&
540 svc.getUuid().equals(uuid)) {
541 return svc;
542 }
543 }
544 return null;
545 }
546
547
548 /**
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700549 * Returns a characteristic with id equal to instanceId.
550 * @hide
551 */
552 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
553 for(BluetoothGattService svc : mServices) {
554 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700555 if (charac.getInstanceId() == instanceId)
556 return charac;
557 }
558 }
559 return null;
560 }
561
562 /**
563 * Returns a descriptor with id equal to instanceId.
564 * @hide
565 */
566 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
567 for(BluetoothGattService svc : mServices) {
568 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
569 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
570 if (desc.getInstanceId() == instanceId)
571 return desc;
572 }
573 }
574 }
575 return null;
576 }
577
578 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800579 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800580 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800581 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
582 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800583 *
584 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
585 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800586 * @param callback GATT callback handler that will receive asynchronous callbacks.
587 * @return If true, the callback will be called to notify success or failure,
588 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800589 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800590 private boolean registerApp(BluetoothGattCallback callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800591 if (DBG) Log.d(TAG, "registerApp()");
592 if (mService == null) return false;
593
594 mCallback = callback;
595 UUID uuid = UUID.randomUUID();
596 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
597
598 try {
599 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
600 } catch (RemoteException e) {
601 Log.e(TAG,"",e);
602 return false;
603 }
604
605 return true;
606 }
607
608 /**
609 * Unregister the current application and callbacks.
610 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800611 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800612 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
613 if (mService == null || mClientIf == 0) return;
614
615 try {
616 mCallback = null;
617 mService.unregisterClient(mClientIf);
618 mClientIf = 0;
619 } catch (RemoteException e) {
620 Log.e(TAG,"",e);
621 }
622 }
623
624 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800625 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800626 *
627 * <p>The connection may not be established right away, but will be
628 * completed when the remote device is available. A
629 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
630 * invoked when the connection state changes as a result of this function.
631 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700632 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800633 * the remote device, or rather passively scan and finalize the connection
634 * when the remote device is in range/available. Generally, the first ever
635 * connection to a device should be direct (autoConnect set to false) and
636 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800637 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800638 *
639 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
640 *
641 * @param device Remote device to connect to
642 * @param autoConnect Whether to directly connect to the remote device (false)
643 * or to automatically connect as soon as the remote
644 * device becomes available (true).
645 * @return true, if the connection attempt was initiated successfully
646 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800647 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
648 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
649 synchronized(mStateLock) {
650 if (mConnState != CONN_STATE_IDLE) {
651 throw new IllegalStateException("Not idle");
652 }
653 mConnState = CONN_STATE_CONNECTING;
654 }
Sungki Kim636ab032016-05-19 10:18:07 -0700655
656 mAutoConnect = autoConnect;
657
Matthew Xieddf7e472013-03-01 18:41:02 -0800658 if (!registerApp(callback)) {
659 synchronized(mStateLock) {
660 mConnState = CONN_STATE_IDLE;
661 }
662 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800663 return false;
664 }
665
Sungki Kim636ab032016-05-19 10:18:07 -0700666 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800667 return true;
668 }
669
670 /**
671 * Disconnects an established connection, or cancels a connection attempt
672 * currently in progress.
673 *
674 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800675 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800676 public void disconnect() {
677 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800678 if (mService == null || mClientIf == 0) return;
679
680 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800681 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682 } catch (RemoteException e) {
683 Log.e(TAG,"",e);
684 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700685 }
686
687 /**
688 * Connect back to remote device.
689 *
690 * <p>This method is used to re-connect to a remote device after the
691 * connection has been dropped. If the device is not in range, the
692 * re-connection will be triggered once the device is back in range.
693 *
694 * @return true, if the connection attempt was initiated successfully
695 */
696 public boolean connect() {
697 try {
698 mService.clientConnect(mClientIf, mDevice.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700699 false, mTransport); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700700 return true;
701 } catch (RemoteException e) {
702 Log.e(TAG,"",e);
703 return false;
704 }
705 }
706
707 /**
708 * Return the remote bluetooth device this GATT client targets to
709 *
710 * @return remote bluetooth device
711 */
712 public BluetoothDevice getDevice() {
713 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800714 }
715
716 /**
717 * Discovers services offered by a remote device as well as their
718 * characteristics and descriptors.
719 *
720 * <p>This is an asynchronous operation. Once service discovery is completed,
721 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
722 * triggered. If the discovery was successful, the remote services can be
723 * retrieved using the {@link #getServices} function.
724 *
725 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
726 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800727 * @return true, if the remote service discovery has been started
728 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800729 public boolean discoverServices() {
730 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800731 if (mService == null || mClientIf == 0) return false;
732
733 mServices.clear();
734
735 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800736 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800737 } catch (RemoteException e) {
738 Log.e(TAG,"",e);
739 return false;
740 }
741
742 return true;
743 }
744
745 /**
746 * Returns a list of GATT services offered by the remote device.
747 *
748 * <p>This function requires that service discovery has been completed
749 * for the given device.
750 *
751 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
752 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800753 * @return List of services on the remote device. Returns an empty list
754 * if service discovery has not yet been performed.
755 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800756 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800757 List<BluetoothGattService> result =
758 new ArrayList<BluetoothGattService>();
759
760 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800761 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800762 result.add(service);
763 }
764 }
765
766 return result;
767 }
768
769 /**
770 * Returns a {@link BluetoothGattService}, if the requested UUID is
771 * supported by the remote device.
772 *
773 * <p>This function requires that service discovery has been completed
774 * for the given device.
775 *
776 * <p>If multiple instances of the same service (as identified by UUID)
777 * exist, the first instance of the service is returned.
778 *
779 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
780 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800781 * @param uuid UUID of the requested service
782 * @return BluetoothGattService if supported, or null if the requested
783 * service is not offered by the remote device.
784 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800785 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800786 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800787 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800788 service.getUuid().equals(uuid)) {
789 return service;
790 }
791 }
792
793 return null;
794 }
795
796 /**
797 * Reads the requested characteristic from the associated remote device.
798 *
799 * <p>This is an asynchronous operation. The result of the read operation
800 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
801 * callback.
802 *
803 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
804 *
805 * @param characteristic Characteristic to read from the remote device
806 * @return true, if the read operation was initiated successfully
807 */
808 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
809 if ((characteristic.getProperties() &
810 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
811
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700812 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800813 if (mService == null || mClientIf == 0) return false;
814
815 BluetoothGattService service = characteristic.getService();
816 if (service == null) return false;
817
818 BluetoothDevice device = service.getDevice();
819 if (device == null) return false;
820
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700821 synchronized(mDeviceBusy) {
822 if (mDeviceBusy) return false;
823 mDeviceBusy = true;
824 }
825
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800826 try {
827 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700828 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800829 } catch (RemoteException e) {
830 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700831 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800832 return false;
833 }
834
835 return true;
836 }
837
838 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800839 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800840 *
841 * <p>Once the write operation has been completed, the
842 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
843 * reporting the result of the operation.
844 *
845 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
846 *
847 * @param characteristic Characteristic to write on the remote device
848 * @return true, if the write operation was initiated successfully
849 */
850 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
851 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
852 && (characteristic.getProperties() &
853 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
854
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700855 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800856 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800857
858 BluetoothGattService service = characteristic.getService();
859 if (service == null) return false;
860
861 BluetoothDevice device = service.getDevice();
862 if (device == null) return false;
863
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700864 synchronized(mDeviceBusy) {
865 if (mDeviceBusy) return false;
866 mDeviceBusy = true;
867 }
868
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800869 try {
870 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700871 characteristic.getInstanceId(), characteristic.getWriteType(),
872 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800873 } catch (RemoteException e) {
874 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700875 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800876 return false;
877 }
878
879 return true;
880 }
881
882 /**
883 * Reads the value for a given descriptor from the associated remote device.
884 *
885 * <p>Once the read operation has been completed, the
886 * {@link BluetoothGattCallback#onDescriptorRead} callback is
887 * triggered, signaling the result of the operation.
888 *
889 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
890 *
891 * @param descriptor Descriptor value to read from the remote device
892 * @return true, if the read operation was initiated successfully
893 */
894 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700895 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800896 if (mService == null || mClientIf == 0) return false;
897
898 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
899 if (characteristic == null) return false;
900
901 BluetoothGattService service = characteristic.getService();
902 if (service == null) return false;
903
904 BluetoothDevice device = service.getDevice();
905 if (device == null) return false;
906
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700907 synchronized(mDeviceBusy) {
908 if (mDeviceBusy) return false;
909 mDeviceBusy = true;
910 }
911
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800912 try {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700913 mService.readDescriptor(mClientIf, device.getAddress(),
914 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800915 } catch (RemoteException e) {
916 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700917 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800918 return false;
919 }
920
921 return true;
922 }
923
924 /**
925 * Write the value of a given descriptor to the associated remote device.
926 *
927 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
928 * triggered to report the result of the write operation.
929 *
930 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
931 *
932 * @param descriptor Descriptor to write to the associated remote device
933 * @return true, if the write operation was initiated successfully
934 */
935 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700936 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800937 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800938
939 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
940 if (characteristic == null) return false;
941
942 BluetoothGattService service = characteristic.getService();
943 if (service == null) return false;
944
945 BluetoothDevice device = service.getDevice();
946 if (device == null) return false;
947
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700948 synchronized(mDeviceBusy) {
949 if (mDeviceBusy) return false;
950 mDeviceBusy = true;
951 }
952
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800953 try {
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -0700954 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jakub Pawlowski8e970d62016-03-30 22:58:17 -0700955 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800956 } catch (RemoteException e) {
957 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700958 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800959 return false;
960 }
961
962 return true;
963 }
964
965 /**
966 * Initiates a reliable write transaction for a given remote device.
967 *
968 * <p>Once a reliable write transaction has been initiated, all calls
969 * to {@link #writeCharacteristic} are sent to the remote device for
970 * verification and queued up for atomic execution. The application will
971 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
972 * in response to every {@link #writeCharacteristic} call and is responsible
973 * for verifying if the value has been transmitted accurately.
974 *
975 * <p>After all characteristics have been queued up and verified,
976 * {@link #executeReliableWrite} will execute all writes. If a characteristic
977 * was not written correctly, calling {@link #abortReliableWrite} will
978 * cancel the current transaction without commiting any values on the
979 * remote device.
980 *
981 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
982 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800983 * @return true, if the reliable write transaction has been initiated
984 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800985 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700986 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800987 if (mService == null || mClientIf == 0) return false;
988
989 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800990 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800991 } catch (RemoteException e) {
992 Log.e(TAG,"",e);
993 return false;
994 }
995
996 return true;
997 }
998
999 /**
1000 * Executes a reliable write transaction for a given remote device.
1001 *
1002 * <p>This function will commit all queued up characteristic write
1003 * operations for a given remote device.
1004 *
1005 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
1006 * invoked to indicate whether the transaction has been executed correctly.
1007 *
1008 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1009 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001010 * @return true, if the request to execute the transaction has been sent
1011 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001012 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001013 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001014 if (mService == null || mClientIf == 0) return false;
1015
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001016 synchronized(mDeviceBusy) {
1017 if (mDeviceBusy) return false;
1018 mDeviceBusy = true;
1019 }
1020
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001021 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001022 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001023 } catch (RemoteException e) {
1024 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001025 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001026 return false;
1027 }
1028
1029 return true;
1030 }
1031
1032 /**
1033 * Cancels a reliable write transaction for a given device.
1034 *
1035 * <p>Calling this function will discard all queued characteristic write
1036 * operations for a given remote device.
1037 *
1038 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001039 */
John Du48f8b5d2013-08-19 12:20:37 -07001040 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001041 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001042 if (mService == null || mClientIf == 0) return;
1043
1044 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001045 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001046 } catch (RemoteException e) {
1047 Log.e(TAG,"",e);
1048 }
1049 }
1050
1051 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001052 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001053 */
1054 public void abortReliableWrite(BluetoothDevice mDevice) {
1055 abortReliableWrite();
1056 }
1057
1058 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001059 * Enable or disable notifications/indications for a given characteristic.
1060 *
1061 * <p>Once notifications are enabled for a characteristic, a
1062 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
1063 * triggered if the remote device indicates that the given characteristic
1064 * has changed.
1065 *
1066 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1067 *
1068 * @param characteristic The characteristic for which to enable notifications
1069 * @param enable Set to true to enable notifications/indications
1070 * @return true, if the requested notification status was set successfully
1071 */
1072 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1073 boolean enable) {
1074 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1075 + " enable: " + enable);
1076 if (mService == null || mClientIf == 0) return false;
1077
1078 BluetoothGattService service = characteristic.getService();
1079 if (service == null) return false;
1080
1081 BluetoothDevice device = service.getDevice();
1082 if (device == null) return false;
1083
1084 try {
1085 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskia9e27ec2016-03-17 16:00:16 -07001086 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001087 } catch (RemoteException e) {
1088 Log.e(TAG,"",e);
1089 return false;
1090 }
1091
1092 return true;
1093 }
1094
1095 /**
1096 * Clears the internal cache and forces a refresh of the services from the
1097 * remote device.
1098 * @hide
1099 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001100 public boolean refresh() {
1101 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001102 if (mService == null || mClientIf == 0) return false;
1103
1104 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001105 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001106 } catch (RemoteException e) {
1107 Log.e(TAG,"",e);
1108 return false;
1109 }
1110
1111 return true;
1112 }
1113
1114 /**
1115 * Read the RSSI for a connected remote device.
1116 *
1117 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
1118 * invoked when the RSSI value has been read.
1119 *
1120 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1121 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001122 * @return true, if the RSSI value has been requested successfully
1123 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001124 public boolean readRemoteRssi() {
1125 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001126 if (mService == null || mClientIf == 0) return false;
1127
1128 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001129 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001130 } catch (RemoteException e) {
1131 Log.e(TAG,"",e);
1132 return false;
1133 }
1134
1135 return true;
1136 }
1137
1138 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001139 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001140 *
1141 * <p>When performing a write request operation (write without response),
1142 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001143 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001144 *
Andre Eisenbach4072da02014-08-19 17:58:55 -07001145 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001146 * whether this operation was successful.
1147 *
1148 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1149 *
1150 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001151 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001152 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001153 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1154 + " mtu: " + mtu);
1155 if (mService == null || mClientIf == 0) return false;
1156
1157 try {
1158 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1159 } catch (RemoteException e) {
1160 Log.e(TAG,"",e);
1161 return false;
1162 }
1163
1164 return true;
1165 }
1166
1167 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001168 * Request a connection parameter update.
1169 *
1170 * <p>This function will send a connection parameter update request to the
1171 * remote device.
1172 *
1173 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001174 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1175 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1176 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001177 * @throws IllegalArgumentException If the parameters are outside of their
1178 * specified range.
1179 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001180 public boolean requestConnectionPriority(int connectionPriority) {
1181 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1182 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001183 throw new IllegalArgumentException("connectionPriority not within valid range");
1184 }
1185
Andre Eisenbach4072da02014-08-19 17:58:55 -07001186 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001187 if (mService == null || mClientIf == 0) return false;
1188
1189 try {
1190 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1191 } catch (RemoteException e) {
1192 Log.e(TAG,"",e);
1193 return false;
1194 }
1195
1196 return true;
1197 }
1198
1199 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001200 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1201 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001202 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001203 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001204 */
1205 @Override
1206 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001207 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001208 }
1209
1210 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001211 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1212 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001213 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001214 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001215 */
1216 @Override
1217 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001218 throw new UnsupportedOperationException
1219 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001220 }
1221
1222 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001223 * Not supported - please use
1224 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1225 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001226 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001227 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001228 */
1229 @Override
1230 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001231 throw new UnsupportedOperationException
1232 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 }
1234}