blob: 11dbf70e1f30acd406c0649e133b2d5e8c4c441d [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 *
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080034 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallbackExt}
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;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080045 private BluetoothGattCallbackExt mCallback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080046 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;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080054 private int mPhy;
Matthew Xieddf7e472013-03-01 18:41:02 -080055
Jacky Cheung3854e222016-10-20 13:55:21 -070056 private static final int AUTH_RETRY_STATE_IDLE = 0;
57 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
58 private static final int AUTH_RETRY_STATE_MITM = 2;
59
Matthew Xieddf7e472013-03-01 18:41:02 -080060 private static final int CONN_STATE_IDLE = 0;
61 private static final int CONN_STATE_CONNECTING = 1;
62 private static final int CONN_STATE_CONNECTED = 2;
63 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070064 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065
66 private List<BluetoothGattService> mServices;
67
Matthew Xieddf7e472013-03-01 18:41:02 -080068 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080069 public static final int GATT_SUCCESS = 0;
70
Matthew Xieddf7e472013-03-01 18:41:02 -080071 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080072 public static final int GATT_READ_NOT_PERMITTED = 0x2;
73
Matthew Xieddf7e472013-03-01 18:41:02 -080074 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080075 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
76
77 /** Insufficient authentication for a given operation */
78 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
79
80 /** The given request is not supported */
81 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
82
83 /** Insufficient encryption for a given operation */
84 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
85
86 /** A read or write operation was requested with an invalid offset */
87 public static final int GATT_INVALID_OFFSET = 0x7;
88
89 /** A write operation exceeds the maximum length of the attribute */
90 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
91
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070092 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070093 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
94
Matthew Xie90ca8072013-05-28 21:06:50 +000095 /** A GATT operation failed, errors other than the above */
96 public static final int GATT_FAILURE = 0x101;
97
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080098 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -070099 * Connection paramter update - Use the connection paramters recommended by the
100 * Bluetooth SIG. This is the default value if no connection parameter update
101 * is requested.
102 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700103 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700104
105 /**
106 * Connection paramter update - Request a high priority, low latency connection.
107 * An application should only request high priority connection paramters to transfer
108 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700109 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700110 * to reduce energy use.
111 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700112 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700113
114 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700115 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700116
117 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800118 * No authentication required.
119 * @hide
120 */
121 /*package*/ static final int AUTHENTICATION_NONE = 0;
122
123 /**
124 * Authentication requested; no man-in-the-middle protection required.
125 * @hide
126 */
127 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
128
129 /**
130 * Authentication with man-in-the-middle protection requested.
131 * @hide
132 */
133 /*package*/ static final int AUTHENTICATION_MITM = 2;
134
135 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800136 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallbackExt implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 */
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800138 private final IBluetoothGattCallbackExt mBluetoothGattCallbackExt =
139 new IBluetoothGattCallbackExt.Stub() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800140 /**
141 * Application interface registered - app is ready to go
142 * @hide
143 */
144 public void onClientRegistered(int status, int clientIf) {
145 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
146 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800147 if (VDBG) {
148 synchronized(mStateLock) {
149 if (mConnState != CONN_STATE_CONNECTING) {
150 Log.e(TAG, "Bad connection state: " + mConnState);
151 }
152 }
153 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800154 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800155 if (status != GATT_SUCCESS) {
Matthew Xie33ec9842013-04-03 00:29:27 -0700156 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
Matthew Xieddf7e472013-03-01 18:41:02 -0800157 BluetoothProfile.STATE_DISCONNECTED);
158 synchronized(mStateLock) {
159 mConnState = CONN_STATE_IDLE;
160 }
161 return;
162 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800163 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800164 mService.clientConnect(mClientIf, mDevice.getAddress(),
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800165 !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800166 } catch (RemoteException e) {
167 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800168 }
169 }
170
171 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800172 * Phy update callback
173 * @hide
174 */
175 @Override
176 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
177 if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status
178 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
179 if (!address.equals(mDevice.getAddress())) {
180 return;
181 }
182
183 try {
184 mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
185 } catch (Exception ex) {
186 Log.w(TAG, "Unhandled exception in callback", ex);
187 }
188 }
189
190 /**
191 * Phy read callback
192 * @hide
193 */
194 @Override
195 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
196 if (DBG) Log.d(TAG, "onPhyRead() - status=" + status
197 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
198 if (!address.equals(mDevice.getAddress())) {
199 return;
200 }
201
202 try {
203 mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
204 } catch (Exception ex) {
205 Log.w(TAG, "Unhandled exception in callback", ex);
206 }
207 }
208
209 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800210 * Client connection state changed
211 * @hide
212 */
213 public void onClientConnectionState(int status, int clientIf,
214 boolean connected, String address) {
215 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
216 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800217 if (!address.equals(mDevice.getAddress())) {
218 return;
219 }
220 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
221 BluetoothProfile.STATE_DISCONNECTED;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800222 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700223 mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800224 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700225 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800226 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800227
228 synchronized(mStateLock) {
229 if (connected) {
230 mConnState = CONN_STATE_CONNECTED;
231 } else {
232 mConnState = CONN_STATE_IDLE;
233 }
234 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700235
236 synchronized(mDeviceBusy) {
237 mDeviceBusy = false;
238 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800239 }
240
241 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800242 * Remote search has been completed.
243 * The internal object structure should now reflect the state
244 * of the remote device database. Let the application know that
245 * we are done at this point.
246 * @hide
247 */
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800248 public void onSearchComplete(String address, List<BluetoothGattService> services,
249 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800250 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800251 if (!address.equals(mDevice.getAddress())) {
252 return;
253 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800254
255 for (BluetoothGattService s : services) {
256 //services we receive don't have device set properly.
257 s.setDevice(mDevice);
258 }
259
260 mServices.addAll(services);
261
262 // Fix references to included services, as they doesn't point to right objects.
263 for (BluetoothGattService fixedService : mServices) {
264 ArrayList<BluetoothGattService> includedServices =
265 new ArrayList(fixedService.getIncludedServices());
266 fixedService.getIncludedServices().clear();
267
268 for(BluetoothGattService brokenRef : includedServices) {
269 BluetoothGattService includedService = getService(mDevice,
270 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
271 if (includedService != null) {
272 fixedService.addIncludedService(includedService);
273 } else {
274 Log.e(TAG, "Broken GATT database: can't find included service.");
275 }
276 }
277 }
278
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800279 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700280 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800281 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700282 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800283 }
284 }
285
286 /**
287 * Remote characteristic has been read.
288 * Updates the internal value.
289 * @hide
290 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700291 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700292 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700293 + " handle=" + handle + " Status=" + status);
294
Matthew Xieddf7e472013-03-01 18:41:02 -0800295 if (!address.equals(mDevice.getAddress())) {
296 return;
297 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700298
299 synchronized(mDeviceBusy) {
300 mDeviceBusy = false;
301 }
302
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800303 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
304 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700305 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700307 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
308 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
309 mService.readCharacteristic(mClientIf, address, handle, authReq);
310 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800311 return;
312 } catch (RemoteException e) {
313 Log.e(TAG,"",e);
314 }
315 }
316
Jacky Cheung3854e222016-10-20 13:55:21 -0700317 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800318
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700319 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
320 if (characteristic == null) {
321 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
322 return;
323 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800324
325 if (status == 0) characteristic.setValue(value);
326
327 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700328 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800329 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700330 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800331 }
332 }
333
334 /**
335 * Characteristic has been written to the remote device.
336 * Let the app know how we did...
337 * @hide
338 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700339 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700340 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700341 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800342
Matthew Xieddf7e472013-03-01 18:41:02 -0800343 if (!address.equals(mDevice.getAddress())) {
344 return;
345 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700346
347 synchronized(mDeviceBusy) {
348 mDeviceBusy = false;
349 }
350
Jakub Pawlowskic9d13c32016-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 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
355 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700356 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800357 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700358 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
359 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700360 mService.writeCharacteristic(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700361 characteristic.getWriteType(), authReq, characteristic.getValue());
362 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800363 return;
364 } catch (RemoteException e) {
365 Log.e(TAG,"",e);
366 }
367 }
368
Jacky Cheung3854e222016-10-20 13:55:21 -0700369 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800370
371 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700372 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800373 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700374 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800375 }
376 }
377
378 /**
379 * Remote characteristic has been updated.
380 * Updates the internal value.
381 * @hide
382 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700383 public void onNotify(String address, int handle, byte[] value) {
384 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800385
Matthew Xieddf7e472013-03-01 18:41:02 -0800386 if (!address.equals(mDevice.getAddress())) {
387 return;
388 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800389
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700390 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800391 if (characteristic == null) return;
392
393 characteristic.setValue(value);
394
395 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700396 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800397 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700398 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800399 }
400 }
401
402 /**
403 * Descriptor has been read.
404 * @hide
405 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700406 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
407 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800408
Matthew Xieddf7e472013-03-01 18:41:02 -0800409 if (!address.equals(mDevice.getAddress())) {
410 return;
411 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700412
413 synchronized(mDeviceBusy) {
414 mDeviceBusy = false;
415 }
416
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700417 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800418 if (descriptor == null) return;
419
420 if (status == 0) descriptor.setValue(value);
421
422 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
423 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700424 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800425 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700426 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
427 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
428 mService.readDescriptor(mClientIf, address, handle, authReq);
429 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700430 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800431 } catch (RemoteException e) {
432 Log.e(TAG,"",e);
433 }
434 }
435
Jacky Cheung3854e222016-10-20 13:55:21 -0700436 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800437
438 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700439 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700441 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800442 }
443 }
444
445 /**
446 * Descriptor write operation complete.
447 * @hide
448 */
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700449 public void onDescriptorWrite(String address, int status, int handle) {
450 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800451
Matthew Xieddf7e472013-03-01 18:41:02 -0800452 if (!address.equals(mDevice.getAddress())) {
453 return;
454 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700455
456 synchronized(mDeviceBusy) {
457 mDeviceBusy = false;
458 }
459
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700460 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800461 if (descriptor == null) return;
462
463 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
464 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700465 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800466 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700467 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
468 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700469 mService.writeDescriptor(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700470 authReq, descriptor.getValue());
471 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700472 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800473 } catch (RemoteException e) {
474 Log.e(TAG,"",e);
475 }
476 }
477
Jacky Cheung3854e222016-10-20 13:55:21 -0700478 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800479
480 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700481 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, 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 }
486
487 /**
488 * Prepared write transaction completed (or aborted)
489 * @hide
490 */
491 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700492 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800493 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800494 if (!address.equals(mDevice.getAddress())) {
495 return;
496 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700497
498 synchronized(mDeviceBusy) {
499 mDeviceBusy = false;
500 }
501
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800502 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700503 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700505 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800506 }
507 }
508
509 /**
510 * Remote device RSSI has been read
511 * @hide
512 */
513 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700514 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800515 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800516 if (!address.equals(mDevice.getAddress())) {
517 return;
518 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 try {
Matthew Xie33ec9842013-04-03 00:29:27 -0700520 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800521 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700522 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 }
524 }
Wei Wangf3055892014-03-11 22:22:41 -0700525
526 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700527 * Callback invoked when the MTU for a given connection changes
528 * @hide
529 */
530 public void onConfigureMTU(String address, int mtu, int status) {
531 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
532 " mtu=" + mtu + " status=" + status);
533 if (!address.equals(mDevice.getAddress())) {
534 return;
535 }
536 try {
Andre Eisenbach4072da02014-08-19 17:58:55 -0700537 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700538 } catch (Exception ex) {
539 Log.w(TAG, "Unhandled exception in callback", ex);
540 }
Wei Wangf3055892014-03-11 22:22:41 -0700541 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800542 };
543
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700544 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800545 int transport, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800546 mService = iGatt;
547 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700548 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800549 mPhy = phy;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800550 mServices = new ArrayList<BluetoothGattService>();
551
Matthew Xieddf7e472013-03-01 18:41:02 -0800552 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700553 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800554 }
555
556 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700557 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700558 *
559 * Application should call this method as early as possible after it is done with
560 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800561 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700562 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800563 if (DBG) Log.d(TAG, "close()");
564
565 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700566 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700567 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800568 }
569
570 /**
571 * Returns a service by UUID, instance and type.
572 * @hide
573 */
574 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
575 int instanceId, int type) {
576 for(BluetoothGattService svc : mServices) {
577 if (svc.getDevice().equals(device) &&
578 svc.getType() == type &&
579 svc.getInstanceId() == instanceId &&
580 svc.getUuid().equals(uuid)) {
581 return svc;
582 }
583 }
584 return null;
585 }
586
587
588 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700589 * Returns a characteristic with id equal to instanceId.
590 * @hide
591 */
592 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
593 for(BluetoothGattService svc : mServices) {
594 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700595 if (charac.getInstanceId() == instanceId)
596 return charac;
597 }
598 }
599 return null;
600 }
601
602 /**
603 * Returns a descriptor with id equal to instanceId.
604 * @hide
605 */
606 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
607 for(BluetoothGattService svc : mServices) {
608 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
609 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
610 if (desc.getInstanceId() == instanceId)
611 return desc;
612 }
613 }
614 }
615 return null;
616 }
617
618 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800619 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800620 *
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800621 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallbackExt#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800622 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800623 *
624 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
625 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800626 * @param callback GATT callback handler that will receive asynchronous callbacks.
627 * @return If true, the callback will be called to notify success or failure,
628 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800629 */
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800630 private boolean registerApp(BluetoothGattCallbackExt callback) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800631 if (DBG) Log.d(TAG, "registerApp()");
632 if (mService == null) return false;
633
634 mCallback = callback;
635 UUID uuid = UUID.randomUUID();
636 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
637
638 try {
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800639 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallbackExt);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800640 } catch (RemoteException e) {
641 Log.e(TAG,"",e);
642 return false;
643 }
644
645 return true;
646 }
647
648 /**
649 * Unregister the current application and callbacks.
650 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800651 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800652 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
653 if (mService == null || mClientIf == 0) return;
654
655 try {
656 mCallback = null;
657 mService.unregisterClient(mClientIf);
658 mClientIf = 0;
659 } catch (RemoteException e) {
660 Log.e(TAG,"",e);
661 }
662 }
663
664 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800665 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800666 *
667 * <p>The connection may not be established right away, but will be
668 * completed when the remote device is available. A
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800669 * {@link BluetoothGattCallbackExt#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800670 * invoked when the connection state changes as a result of this function.
671 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700672 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800673 * the remote device, or rather passively scan and finalize the connection
674 * when the remote device is in range/available. Generally, the first ever
675 * connection to a device should be direct (autoConnect set to false) and
676 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800677 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800678 *
679 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
680 *
681 * @param device Remote device to connect to
682 * @param autoConnect Whether to directly connect to the remote device (false)
683 * or to automatically connect as soon as the remote
684 * device becomes available (true).
685 * @return true, if the connection attempt was initiated successfully
686 */
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800687 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallbackExt callback) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800688 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
689 synchronized(mStateLock) {
690 if (mConnState != CONN_STATE_IDLE) {
691 throw new IllegalStateException("Not idle");
692 }
693 mConnState = CONN_STATE_CONNECTING;
694 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700695
696 mAutoConnect = autoConnect;
697
Matthew Xieddf7e472013-03-01 18:41:02 -0800698 if (!registerApp(callback)) {
699 synchronized(mStateLock) {
700 mConnState = CONN_STATE_IDLE;
701 }
702 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800703 return false;
704 }
705
Sungki Kimd35167a2016-05-19 10:18:07 -0700706 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800707 return true;
708 }
709
710 /**
711 * Disconnects an established connection, or cancels a connection attempt
712 * currently in progress.
713 *
714 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800715 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800716 public void disconnect() {
717 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800718 if (mService == null || mClientIf == 0) return;
719
720 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800721 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800722 } catch (RemoteException e) {
723 Log.e(TAG,"",e);
724 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700725 }
726
727 /**
728 * Connect back to remote device.
729 *
730 * <p>This method is used to re-connect to a remote device after the
731 * connection has been dropped. If the device is not in range, the
732 * re-connection will be triggered once the device is back in range.
733 *
734 * @return true, if the connection attempt was initiated successfully
735 */
736 public boolean connect() {
737 try {
738 mService.clientConnect(mClientIf, mDevice.getAddress(),
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800739 false, mTransport, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700740 return true;
741 } catch (RemoteException e) {
742 Log.e(TAG,"",e);
743 return false;
744 }
745 }
746
747 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800748 * Set the preferred connection PHY for this app. Please note that this is just a
749 * recommendation, wether the PHY change will happen depends on other applications peferences,
750 * local and remote controller capabilities. Controller can override these settings.
751 * <p>
752 * {@link BluetoothGattCallbackExt#onPhyUpdate} will be triggered as a result of this call, even
753 * if no PHY change happens. It is also triggered when remote device updates the PHY.
754 *
755 * @param txPhy preferred transmitter PHY. Bitwise OR of any of
756 * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
757 * {@link BluetoothDevice#PHY_LE_CODED}.
758 * @param rxPhy preferred receiver PHY. Bitwise OR of any of
759 * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
760 * {@link BluetoothDevice#PHY_LE_CODED}.
761 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
762 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
763 * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
764 */
765 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
766 try {
767 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
768 phyOptions);
769 } catch (RemoteException e) {
770 Log.e(TAG,"",e);
771 }
772 }
773
774 /**
775 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
776 * in {@link BluetoothGattCallbackExt#onPhyRead}
777 */
778 public void readPhy() {
779 try {
780 mService.clientReadPhy(mClientIf, mDevice.getAddress());
781 } catch (RemoteException e) {
782 Log.e(TAG,"",e);
783 }
784 }
785
786 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700787 * Return the remote bluetooth device this GATT client targets to
788 *
789 * @return remote bluetooth device
790 */
791 public BluetoothDevice getDevice() {
792 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800793 }
794
795 /**
796 * Discovers services offered by a remote device as well as their
797 * characteristics and descriptors.
798 *
799 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800800 * the {@link BluetoothGattCallbackExt#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800801 * triggered. If the discovery was successful, the remote services can be
802 * retrieved using the {@link #getServices} function.
803 *
804 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
805 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 * @return true, if the remote service discovery has been started
807 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800808 public boolean discoverServices() {
809 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800810 if (mService == null || mClientIf == 0) return false;
811
812 mServices.clear();
813
814 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800815 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800816 } catch (RemoteException e) {
817 Log.e(TAG,"",e);
818 return false;
819 }
820
821 return true;
822 }
823
824 /**
825 * Returns a list of GATT services offered by the remote device.
826 *
827 * <p>This function requires that service discovery has been completed
828 * for the given device.
829 *
830 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
831 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800832 * @return List of services on the remote device. Returns an empty list
833 * if service discovery has not yet been performed.
834 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800835 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800836 List<BluetoothGattService> result =
837 new ArrayList<BluetoothGattService>();
838
839 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800840 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800841 result.add(service);
842 }
843 }
844
845 return result;
846 }
847
848 /**
849 * Returns a {@link BluetoothGattService}, if the requested UUID is
850 * supported by the remote device.
851 *
852 * <p>This function requires that service discovery has been completed
853 * for the given device.
854 *
855 * <p>If multiple instances of the same service (as identified by UUID)
856 * exist, the first instance of the service is returned.
857 *
858 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
859 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800860 * @param uuid UUID of the requested service
861 * @return BluetoothGattService if supported, or null if the requested
862 * service is not offered by the remote device.
863 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800864 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800865 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800866 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800867 service.getUuid().equals(uuid)) {
868 return service;
869 }
870 }
871
872 return null;
873 }
874
875 /**
876 * Reads the requested characteristic from the associated remote device.
877 *
878 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800879 * is reported by the {@link BluetoothGattCallbackExt#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800880 * callback.
881 *
882 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
883 *
884 * @param characteristic Characteristic to read from the remote device
885 * @return true, if the read operation was initiated successfully
886 */
887 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
888 if ((characteristic.getProperties() &
889 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
890
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700891 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800892 if (mService == null || mClientIf == 0) return false;
893
894 BluetoothGattService service = characteristic.getService();
895 if (service == null) return false;
896
897 BluetoothDevice device = service.getDevice();
898 if (device == null) return false;
899
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700900 synchronized(mDeviceBusy) {
901 if (mDeviceBusy) return false;
902 mDeviceBusy = true;
903 }
904
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800905 try {
906 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700907 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800908 } catch (RemoteException e) {
909 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700910 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800911 return false;
912 }
913
914 return true;
915 }
916
917 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800918 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800919 *
920 * <p>Once the write operation has been completed, the
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800921 * {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800922 * reporting the result of the operation.
923 *
924 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
925 *
926 * @param characteristic Characteristic to write on the remote device
927 * @return true, if the write operation was initiated successfully
928 */
929 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
930 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
931 && (characteristic.getProperties() &
932 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
933
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700934 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -0800935 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800936
937 BluetoothGattService service = characteristic.getService();
938 if (service == null) return false;
939
940 BluetoothDevice device = service.getDevice();
941 if (device == null) return false;
942
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700943 synchronized(mDeviceBusy) {
944 if (mDeviceBusy) return false;
945 mDeviceBusy = true;
946 }
947
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800948 try {
949 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700950 characteristic.getInstanceId(), characteristic.getWriteType(),
951 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800952 } catch (RemoteException e) {
953 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700954 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800955 return false;
956 }
957
958 return true;
959 }
960
961 /**
962 * Reads the value for a given descriptor from the associated remote device.
963 *
964 * <p>Once the read operation has been completed, the
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800965 * {@link BluetoothGattCallbackExt#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800966 * triggered, signaling the result of the operation.
967 *
968 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
969 *
970 * @param descriptor Descriptor value to read from the remote device
971 * @return true, if the read operation was initiated successfully
972 */
973 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700974 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800975 if (mService == null || mClientIf == 0) return false;
976
977 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
978 if (characteristic == null) return false;
979
980 BluetoothGattService service = characteristic.getService();
981 if (service == null) return false;
982
983 BluetoothDevice device = service.getDevice();
984 if (device == null) return false;
985
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700986 synchronized(mDeviceBusy) {
987 if (mDeviceBusy) return false;
988 mDeviceBusy = true;
989 }
990
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800991 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700992 mService.readDescriptor(mClientIf, device.getAddress(),
993 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800994 } catch (RemoteException e) {
995 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700996 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800997 return false;
998 }
999
1000 return true;
1001 }
1002
1003 /**
1004 * Write the value of a given descriptor to the associated remote device.
1005 *
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -08001006 * <p>A {@link BluetoothGattCallbackExt#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001007 * triggered to report the result of the write operation.
1008 *
1009 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1010 *
1011 * @param descriptor Descriptor to write to the associated remote device
1012 * @return true, if the write operation was initiated successfully
1013 */
1014 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001015 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001016 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001017
1018 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1019 if (characteristic == null) return false;
1020
1021 BluetoothGattService service = characteristic.getService();
1022 if (service == null) return false;
1023
1024 BluetoothDevice device = service.getDevice();
1025 if (device == null) return false;
1026
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001027 synchronized(mDeviceBusy) {
1028 if (mDeviceBusy) return false;
1029 mDeviceBusy = true;
1030 }
1031
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001032 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001033 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jakub Pawlowski8e970d62016-03-30 22:58:17 -07001034 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001035 } catch (RemoteException e) {
1036 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001037 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001038 return false;
1039 }
1040
1041 return true;
1042 }
1043
1044 /**
1045 * Initiates a reliable write transaction for a given remote device.
1046 *
1047 * <p>Once a reliable write transaction has been initiated, all calls
1048 * to {@link #writeCharacteristic} are sent to the remote device for
1049 * verification and queued up for atomic execution. The application will
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -08001050 * receive an {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001051 * in response to every {@link #writeCharacteristic} call and is responsible
1052 * for verifying if the value has been transmitted accurately.
1053 *
1054 * <p>After all characteristics have been queued up and verified,
1055 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1056 * was not written correctly, calling {@link #abortReliableWrite} will
1057 * cancel the current transaction without commiting any values on the
1058 * remote device.
1059 *
1060 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1061 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001062 * @return true, if the reliable write transaction has been initiated
1063 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001064 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001065 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001066 if (mService == null || mClientIf == 0) return false;
1067
1068 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001069 mService.beginReliableWrite(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001070 } catch (RemoteException e) {
1071 Log.e(TAG,"",e);
1072 return false;
1073 }
1074
1075 return true;
1076 }
1077
1078 /**
1079 * Executes a reliable write transaction for a given remote device.
1080 *
1081 * <p>This function will commit all queued up characteristic write
1082 * operations for a given remote device.
1083 *
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -08001084 * <p>A {@link BluetoothGattCallbackExt#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001085 * invoked to indicate whether the transaction has been executed correctly.
1086 *
1087 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1088 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001089 * @return true, if the request to execute the transaction has been sent
1090 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001091 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001092 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001093 if (mService == null || mClientIf == 0) return false;
1094
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001095 synchronized(mDeviceBusy) {
1096 if (mDeviceBusy) return false;
1097 mDeviceBusy = true;
1098 }
1099
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001100 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001101 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001102 } catch (RemoteException e) {
1103 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001104 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001105 return false;
1106 }
1107
1108 return true;
1109 }
1110
1111 /**
1112 * Cancels a reliable write transaction for a given device.
1113 *
1114 * <p>Calling this function will discard all queued characteristic write
1115 * operations for a given remote device.
1116 *
1117 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001118 */
John Du48f8b5d2013-08-19 12:20:37 -07001119 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001120 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001121 if (mService == null || mClientIf == 0) return;
1122
1123 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001124 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001125 } catch (RemoteException e) {
1126 Log.e(TAG,"",e);
1127 }
1128 }
1129
1130 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001131 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001132 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001133 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001134 public void abortReliableWrite(BluetoothDevice mDevice) {
1135 abortReliableWrite();
1136 }
1137
1138 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001139 * Enable or disable notifications/indications for a given characteristic.
1140 *
1141 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -08001142 * {@link BluetoothGattCallbackExt#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001143 * triggered if the remote device indicates that the given characteristic
1144 * has changed.
1145 *
1146 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1147 *
1148 * @param characteristic The characteristic for which to enable notifications
1149 * @param enable Set to true to enable notifications/indications
1150 * @return true, if the requested notification status was set successfully
1151 */
1152 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1153 boolean enable) {
1154 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1155 + " enable: " + enable);
1156 if (mService == null || mClientIf == 0) return false;
1157
1158 BluetoothGattService service = characteristic.getService();
1159 if (service == null) return false;
1160
1161 BluetoothDevice device = service.getDevice();
1162 if (device == null) return false;
1163
1164 try {
1165 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001166 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001167 } catch (RemoteException e) {
1168 Log.e(TAG,"",e);
1169 return false;
1170 }
1171
1172 return true;
1173 }
1174
1175 /**
1176 * Clears the internal cache and forces a refresh of the services from the
1177 * remote device.
1178 * @hide
1179 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001180 public boolean refresh() {
1181 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001182 if (mService == null || mClientIf == 0) return false;
1183
1184 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001185 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001186 } catch (RemoteException e) {
1187 Log.e(TAG,"",e);
1188 return false;
1189 }
1190
1191 return true;
1192 }
1193
1194 /**
1195 * Read the RSSI for a connected remote device.
1196 *
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -08001197 * <p>The {@link BluetoothGattCallbackExt#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001198 * invoked when the RSSI value has been read.
1199 *
1200 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1201 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001202 * @return true, if the RSSI value has been requested successfully
1203 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001204 public boolean readRemoteRssi() {
1205 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001206 if (mService == null || mClientIf == 0) return false;
1207
1208 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001209 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001210 } catch (RemoteException e) {
1211 Log.e(TAG,"",e);
1212 return false;
1213 }
1214
1215 return true;
1216 }
1217
1218 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001219 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001220 *
1221 * <p>When performing a write request operation (write without response),
1222 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001223 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001224 *
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -08001225 * <p>A {@link BluetoothGattCallbackExt#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001226 * whether this operation was successful.
1227 *
1228 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1229 *
1230 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001231 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001232 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001233 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1234 + " mtu: " + mtu);
1235 if (mService == null || mClientIf == 0) return false;
1236
1237 try {
1238 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1239 } catch (RemoteException e) {
1240 Log.e(TAG,"",e);
1241 return false;
1242 }
1243
1244 return true;
1245 }
1246
1247 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001248 * Request a connection parameter update.
1249 *
1250 * <p>This function will send a connection parameter update request to the
1251 * remote device.
1252 *
1253 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001254 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1255 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1256 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001257 * @throws IllegalArgumentException If the parameters are outside of their
1258 * specified range.
1259 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001260 public boolean requestConnectionPriority(int connectionPriority) {
1261 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1262 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001263 throw new IllegalArgumentException("connectionPriority not within valid range");
1264 }
1265
Andre Eisenbach4072da02014-08-19 17:58:55 -07001266 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001267 if (mService == null || mClientIf == 0) return false;
1268
1269 try {
1270 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1271 } catch (RemoteException e) {
1272 Log.e(TAG,"",e);
1273 return false;
1274 }
1275
1276 return true;
1277 }
1278
1279 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001280 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1281 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001282 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001283 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001284 */
1285 @Override
1286 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001287 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001288 }
1289
1290 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001291 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1292 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001293 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001294 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001295 */
1296 @Override
1297 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001298 throw new UnsupportedOperationException
1299 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001300 }
1301
1302 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001303 * Not supported - please use
1304 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1305 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001306 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001307 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001308 */
1309 @Override
1310 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001311 throw new UnsupportedOperationException
1312 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001313 }
1314}