blob: 5fabbb68df85a14a9432c7e3c3db085c2d043701 [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;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070020import android.os.Handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080021import android.os.ParcelUuid;
22import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080023import android.util.Log;
24
25import java.util.ArrayList;
26import java.util.List;
27import java.util.UUID;
28
29/**
Matthew Xieddf7e472013-03-01 18:41:02 -080030 * Public API for the Bluetooth GATT Profile.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080031 *
Matthew Xieddf7e472013-03-01 18:41:02 -080032 * <p>This class provides Bluetooth GATT functionality to enable communication
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080033 * with Bluetooth Smart or Smart Ready devices.
34 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070035 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
Matthew Xie33ec9842013-04-03 00:29:27 -070036 * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
Matthew Xieddf7e472013-03-01 18:41:02 -080037 * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
38 * scan process.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080039 */
40public final class BluetoothGatt implements BluetoothProfile {
41 private static final String TAG = "BluetoothGatt";
42 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070043 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080045 private IBluetoothGatt mService;
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070046 private BluetoothGattCallback mCallback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -070047 private Handler mHandler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080048 private int mClientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -080049 private BluetoothDevice mDevice;
50 private boolean mAutoConnect;
Jacky Cheung3854e222016-10-20 13:55:21 -070051 private int mAuthRetryState;
Matthew Xieddf7e472013-03-01 18:41:02 -080052 private int mConnState;
53 private final Object mStateLock = new Object();
Andre Eisenbachcc68cc92014-03-18 14:26:51 -070054 private Boolean mDeviceBusy = false;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070055 private int mTransport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -080056 private int mPhy;
Matthew Xieddf7e472013-03-01 18:41:02 -080057
Jacky Cheung3854e222016-10-20 13:55:21 -070058 private static final int AUTH_RETRY_STATE_IDLE = 0;
59 private static final int AUTH_RETRY_STATE_NO_MITM = 1;
60 private static final int AUTH_RETRY_STATE_MITM = 2;
61
Matthew Xieddf7e472013-03-01 18:41:02 -080062 private static final int CONN_STATE_IDLE = 0;
63 private static final int CONN_STATE_CONNECTING = 1;
64 private static final int CONN_STATE_CONNECTED = 2;
65 private static final int CONN_STATE_DISCONNECTING = 3;
Matthew Xie33ec9842013-04-03 00:29:27 -070066 private static final int CONN_STATE_CLOSED = 4;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080067
68 private List<BluetoothGattService> mServices;
69
Matthew Xieddf7e472013-03-01 18:41:02 -080070 /** A GATT operation completed successfully */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080071 public static final int GATT_SUCCESS = 0;
72
Matthew Xieddf7e472013-03-01 18:41:02 -080073 /** GATT read operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080074 public static final int GATT_READ_NOT_PERMITTED = 0x2;
75
Matthew Xieddf7e472013-03-01 18:41:02 -080076 /** GATT write operation is not permitted */
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080077 public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
78
79 /** Insufficient authentication for a given operation */
80 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5;
81
82 /** The given request is not supported */
83 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6;
84
85 /** Insufficient encryption for a given operation */
86 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf;
87
88 /** A read or write operation was requested with an invalid offset */
89 public static final int GATT_INVALID_OFFSET = 0x7;
90
91 /** A write operation exceeds the maximum length of the attribute */
92 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
93
Andre Eisenbach45a0a1a2014-06-30 11:37:05 -070094 /** A remote device connection is congested. */
Andre Eisenbachdadefda2014-03-28 14:54:53 -070095 public static final int GATT_CONNECTION_CONGESTED = 0x8f;
96
Matthew Xie90ca8072013-05-28 21:06:50 +000097 /** A GATT operation failed, errors other than the above */
98 public static final int GATT_FAILURE = 0x101;
99
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800100 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700101 * Connection paramter update - Use the connection paramters recommended by the
102 * Bluetooth SIG. This is the default value if no connection parameter update
103 * is requested.
104 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700105 public static final int CONNECTION_PRIORITY_BALANCED = 0;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700106
107 /**
108 * Connection paramter update - Request a high priority, low latency connection.
109 * An application should only request high priority connection paramters to transfer
110 * large amounts of data over LE quickly. Once the transfer is complete, the application
Andre Eisenbach4072da02014-08-19 17:58:55 -0700111 * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connectoin parameters
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700112 * to reduce energy use.
113 */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700114 public static final int CONNECTION_PRIORITY_HIGH = 1;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700115
116 /** Connection paramter update - Request low power, reduced data rate connection parameters. */
Andre Eisenbach4072da02014-08-19 17:58:55 -0700117 public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700118
119 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800120 * No authentication required.
121 * @hide
122 */
123 /*package*/ static final int AUTHENTICATION_NONE = 0;
124
125 /**
126 * Authentication requested; no man-in-the-middle protection required.
127 * @hide
128 */
129 /*package*/ static final int AUTHENTICATION_NO_MITM = 1;
130
131 /**
132 * Authentication with man-in-the-middle protection requested.
133 * @hide
134 */
135 /*package*/ static final int AUTHENTICATION_MITM = 2;
136
137 /**
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700138 * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800139 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -0700140 private final IBluetoothGattCallback mBluetoothGattCallback =
141 new IBluetoothGattCallback.Stub() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800142 /**
143 * Application interface registered - app is ready to go
144 * @hide
145 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700146 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800147 public void onClientRegistered(int status, int clientIf) {
148 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
149 + " clientIf=" + clientIf);
Matthew Xieddf7e472013-03-01 18:41:02 -0800150 if (VDBG) {
151 synchronized(mStateLock) {
152 if (mConnState != CONN_STATE_CONNECTING) {
153 Log.e(TAG, "Bad connection state: " + mConnState);
154 }
155 }
156 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800157 mClientIf = clientIf;
Matthew Xieddf7e472013-03-01 18:41:02 -0800158 if (status != GATT_SUCCESS) {
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700159 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700160 @Override
161 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700162 if (mCallback != null) {
163 mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
164 BluetoothProfile.STATE_DISCONNECTED);
165 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700166 }
167 });
168
Matthew Xieddf7e472013-03-01 18:41:02 -0800169 synchronized(mStateLock) {
170 mConnState = CONN_STATE_IDLE;
171 }
172 return;
173 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800174 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800175 mService.clientConnect(mClientIf, mDevice.getAddress(),
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800176 !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xieddf7e472013-03-01 18:41:02 -0800177 } catch (RemoteException e) {
178 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800179 }
180 }
181
182 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800183 * Phy update callback
184 * @hide
185 */
186 @Override
187 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
188 if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status
189 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
190 if (!address.equals(mDevice.getAddress())) {
191 return;
192 }
193
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700194 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700195 @Override
196 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700197 if (mCallback != null) {
198 mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
199 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700200 }
201 });
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800202 }
203
204 /**
205 * Phy read callback
206 * @hide
207 */
208 @Override
209 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
210 if (DBG) Log.d(TAG, "onPhyRead() - status=" + status
211 + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
212 if (!address.equals(mDevice.getAddress())) {
213 return;
214 }
215
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700216 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700217 @Override
218 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700219 if (mCallback != null) {
220 mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
221 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700222 }
223 });
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800224 }
225
226 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800227 * Client connection state changed
228 * @hide
229 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700230 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800231 public void onClientConnectionState(int status, int clientIf,
232 boolean connected, String address) {
233 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
234 + " clientIf=" + clientIf + " device=" + address);
Matthew Xieddf7e472013-03-01 18:41:02 -0800235 if (!address.equals(mDevice.getAddress())) {
236 return;
237 }
238 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
239 BluetoothProfile.STATE_DISCONNECTED;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700240
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700241 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700242 @Override
243 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700244 if (mCallback != null) {
245 mCallback.onConnectionStateChange(BluetoothGatt.this, status,
246 profileState);
247 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700248 }
249 });
Matthew Xieddf7e472013-03-01 18:41:02 -0800250
251 synchronized(mStateLock) {
252 if (connected) {
253 mConnState = CONN_STATE_CONNECTED;
254 } else {
255 mConnState = CONN_STATE_IDLE;
256 }
257 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700258
259 synchronized(mDeviceBusy) {
260 mDeviceBusy = false;
261 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800262 }
263
264 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800265 * Remote search has been completed.
266 * The internal object structure should now reflect the state
267 * of the remote device database. Let the application know that
268 * we are done at this point.
269 * @hide
270 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700271 @Override
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800272 public void onSearchComplete(String address, List<BluetoothGattService> services,
273 int status) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800274 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800275 if (!address.equals(mDevice.getAddress())) {
276 return;
277 }
Jakub Pawlowskibf0faed2016-03-01 18:50:27 -0800278
279 for (BluetoothGattService s : services) {
280 //services we receive don't have device set properly.
281 s.setDevice(mDevice);
282 }
283
284 mServices.addAll(services);
285
286 // Fix references to included services, as they doesn't point to right objects.
287 for (BluetoothGattService fixedService : mServices) {
288 ArrayList<BluetoothGattService> includedServices =
289 new ArrayList(fixedService.getIncludedServices());
290 fixedService.getIncludedServices().clear();
291
292 for(BluetoothGattService brokenRef : includedServices) {
293 BluetoothGattService includedService = getService(mDevice,
294 brokenRef.getUuid(), brokenRef.getInstanceId(), brokenRef.getType());
295 if (includedService != null) {
296 fixedService.addIncludedService(includedService);
297 } else {
298 Log.e(TAG, "Broken GATT database: can't find included service.");
299 }
300 }
301 }
302
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700303 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700304 @Override
305 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700306 if (mCallback != null) {
307 mCallback.onServicesDiscovered(BluetoothGatt.this, status);
308 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700309 }
310 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800311 }
312
313 /**
314 * Remote characteristic has been read.
315 * Updates the internal value.
316 * @hide
317 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700318 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700319 public void onCharacteristicRead(String address, int status, int handle, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700320 if (VDBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700321 + " handle=" + handle + " Status=" + status);
322
Matthew Xieddf7e472013-03-01 18:41:02 -0800323 if (!address.equals(mDevice.getAddress())) {
324 return;
325 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700326
327 synchronized(mDeviceBusy) {
328 mDeviceBusy = false;
329 }
330
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800331 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
332 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700333 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800334 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700335 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
336 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
337 mService.readCharacteristic(mClientIf, address, handle, authReq);
338 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800339 return;
340 } catch (RemoteException e) {
341 Log.e(TAG,"",e);
342 }
343 }
344
Jacky Cheung3854e222016-10-20 13:55:21 -0700345 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700347 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
348 if (characteristic == null) {
349 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!");
350 return;
351 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800352
353 if (status == 0) characteristic.setValue(value);
354
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700355 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700356 @Override
357 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700358 if (mCallback != null) {
359 mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic,
360 status);
361 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700362 }
363 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800364 }
365
366 /**
367 * Characteristic has been written to the remote device.
368 * Let the app know how we did...
369 * @hide
370 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700371 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700372 public void onCharacteristicWrite(String address, int status, int handle) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700373 if (VDBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700374 + " handle=" + handle + " Status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800375
Matthew Xieddf7e472013-03-01 18:41:02 -0800376 if (!address.equals(mDevice.getAddress())) {
377 return;
378 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700379
380 synchronized(mDeviceBusy) {
381 mDeviceBusy = false;
382 }
383
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700384 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800385 if (characteristic == null) return;
386
387 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
388 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700389 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800390 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700391 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
392 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700393 mService.writeCharacteristic(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700394 characteristic.getWriteType(), authReq, characteristic.getValue());
395 mAuthRetryState++;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800396 return;
397 } catch (RemoteException e) {
398 Log.e(TAG,"",e);
399 }
400 }
401
Jacky Cheung3854e222016-10-20 13:55:21 -0700402 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800403
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700404 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700405 @Override
406 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700407 if (mCallback != null) {
408 mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
409 status);
410 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700411 }
412 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800413 }
414
415 /**
416 * Remote characteristic has been updated.
417 * Updates the internal value.
418 * @hide
419 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700420 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700421 public void onNotify(String address, int handle, byte[] value) {
422 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800423
Matthew Xieddf7e472013-03-01 18:41:02 -0800424 if (!address.equals(mDevice.getAddress())) {
425 return;
426 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800427
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700428 BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800429 if (characteristic == null) return;
430
431 characteristic.setValue(value);
432
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700433 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700434 @Override
435 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700436 if (mCallback != null) {
437 mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
438 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700439 }
440 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800441 }
442
443 /**
444 * Descriptor has been read.
445 * @hide
446 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700447 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700448 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
449 if (VDBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800450
Matthew Xieddf7e472013-03-01 18:41:02 -0800451 if (!address.equals(mDevice.getAddress())) {
452 return;
453 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700454
455 synchronized(mDeviceBusy) {
456 mDeviceBusy = false;
457 }
458
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700459 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800460 if (descriptor == null) return;
461
462 if (status == 0) descriptor.setValue(value);
463
464 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
465 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700466 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800467 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700468 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
469 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
470 mService.readDescriptor(mClientIf, address, handle, authReq);
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
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700480 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700481 @Override
482 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700483 if (mCallback != null) {
484 mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
485 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700486 }
487 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800488 }
489
490 /**
491 * Descriptor write operation complete.
492 * @hide
493 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700494 @Override
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700495 public void onDescriptorWrite(String address, int status, int handle) {
496 if (VDBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800497
Matthew Xieddf7e472013-03-01 18:41:02 -0800498 if (!address.equals(mDevice.getAddress())) {
499 return;
500 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700501
502 synchronized(mDeviceBusy) {
503 mDeviceBusy = false;
504 }
505
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700506 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800507 if (descriptor == null) return;
508
509 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
510 || status == GATT_INSUFFICIENT_ENCRYPTION)
Jacky Cheung3854e222016-10-20 13:55:21 -0700511 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800512 try {
Jacky Cheung3854e222016-10-20 13:55:21 -0700513 final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ?
514 AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700515 mService.writeDescriptor(mClientIf, address, handle,
Jacky Cheung3854e222016-10-20 13:55:21 -0700516 authReq, descriptor.getValue());
517 mAuthRetryState++;
Andre Eisenbachd65e8f42014-07-25 15:16:11 -0700518 return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 } catch (RemoteException e) {
520 Log.e(TAG,"",e);
521 }
522 }
523
Jacky Cheung3854e222016-10-20 13:55:21 -0700524 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800525
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700526 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700527 @Override
528 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700529 if (mCallback != null) {
530 mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
531 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700532 }
533 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800534 }
535
536 /**
537 * Prepared write transaction completed (or aborted)
538 * @hide
539 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700540 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800541 public void onExecuteWrite(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700542 if (VDBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800543 + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800544 if (!address.equals(mDevice.getAddress())) {
545 return;
546 }
Andre Eisenbachcc68cc92014-03-18 14:26:51 -0700547
548 synchronized(mDeviceBusy) {
549 mDeviceBusy = false;
550 }
551
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700552 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700553 @Override
554 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700555 if (mCallback != null) {
556 mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
557 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700558 }
559 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800560 }
561
562 /**
563 * Remote device RSSI has been read
564 * @hide
565 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700566 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800567 public void onReadRemoteRssi(String address, int rssi, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700568 if (VDBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800569 " rssi=" + rssi + " status=" + status);
Matthew Xieddf7e472013-03-01 18:41:02 -0800570 if (!address.equals(mDevice.getAddress())) {
571 return;
572 }
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700573 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700574 @Override
575 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700576 if (mCallback != null) {
577 mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
578 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700579 }
580 });
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800581 }
Wei Wangf3055892014-03-11 22:22:41 -0700582
583 /**
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700584 * Callback invoked when the MTU for a given connection changes
585 * @hide
586 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700587 @Override
Andre Eisenbach580b0a12014-03-25 06:31:50 -0700588 public void onConfigureMTU(String address, int mtu, int status) {
589 if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address +
590 " mtu=" + mtu + " status=" + status);
591 if (!address.equals(mDevice.getAddress())) {
592 return;
593 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700594
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700595 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700596 @Override
597 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700598 if (mCallback != null) {
599 mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
600 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700601 }
602 });
Wei Wangf3055892014-03-11 22:22:41 -0700603 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700604
605 /**
606 * Callback invoked when the given connection is updated
607 * @hide
608 */
609 @Override
610 public void onConnectionUpdated(String address, int interval, int latency,
611 int timeout, int status) {
612 if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address +
613 " interval=" + interval + " latency=" + latency +
614 " timeout=" + timeout + " status=" + status);
615 if (!address.equals(mDevice.getAddress())) {
616 return;
617 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700618
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700619 runOrQueueCallback(new Runnable() {
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700620 @Override
621 public void run() {
Jakub Pawlowski0e0e7572017-04-27 07:04:15 -0700622 if (mCallback != null) {
623 mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
624 timeout, status);
625 }
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700626 }
627 });
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700628 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800629 };
630
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700631 /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800632 int transport, int phy) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800633 mService = iGatt;
634 mDevice = device;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700635 mTransport = transport;
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800636 mPhy = phy;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800637 mServices = new ArrayList<BluetoothGattService>();
638
Matthew Xieddf7e472013-03-01 18:41:02 -0800639 mConnState = CONN_STATE_IDLE;
Jacky Cheung3854e222016-10-20 13:55:21 -0700640 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800641 }
642
643 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700644 * Close this Bluetooth GATT client.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700645 *
646 * Application should call this method as early as possible after it is done with
647 * this GATT client.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800648 */
Matthew Xie33ec9842013-04-03 00:29:27 -0700649 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800650 if (DBG) Log.d(TAG, "close()");
651
652 unregisterApp();
Matthew Xie33ec9842013-04-03 00:29:27 -0700653 mConnState = CONN_STATE_CLOSED;
Jacky Cheung3854e222016-10-20 13:55:21 -0700654 mAuthRetryState = AUTH_RETRY_STATE_IDLE;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800655 }
656
657 /**
658 * Returns a service by UUID, instance and type.
659 * @hide
660 */
661 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
662 int instanceId, int type) {
663 for(BluetoothGattService svc : mServices) {
664 if (svc.getDevice().equals(device) &&
665 svc.getType() == type &&
666 svc.getInstanceId() == instanceId &&
667 svc.getUuid().equals(uuid)) {
668 return svc;
669 }
670 }
671 return null;
672 }
673
674
675 /**
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700676 * Returns a characteristic with id equal to instanceId.
677 * @hide
678 */
679 /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
680 for(BluetoothGattService svc : mServices) {
681 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -0700682 if (charac.getInstanceId() == instanceId)
683 return charac;
684 }
685 }
686 return null;
687 }
688
689 /**
690 * Returns a descriptor with id equal to instanceId.
691 * @hide
692 */
693 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) {
694 for(BluetoothGattService svc : mServices) {
695 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
696 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
697 if (desc.getInstanceId() == instanceId)
698 return desc;
699 }
700 }
701 }
702 return null;
703 }
704
705 /**
Ruben Brunk6bdc5502017-05-01 16:57:31 -0700706 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable
707 * immediately if no Handler was provided.
708 */
709 private void runOrQueueCallback(final Runnable cb) {
710 if (mHandler == null) {
711 try {
712 cb.run();
713 } catch (Exception ex) {
714 Log.w(TAG, "Unhandled exception in callback", ex);
715 }
716 } else {
717 mHandler.post(cb);
718 }
719 }
720
721 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800722 * Register an application callback to start using GATT.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800723 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700724 * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
Matthew Xieddf7e472013-03-01 18:41:02 -0800725 * is used to notify success or failure if the function returns true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800726 *
727 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
728 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800729 * @param callback GATT callback handler that will receive asynchronous callbacks.
730 * @return If true, the callback will be called to notify success or failure,
731 * false on immediate error
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800732 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700733 private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800734 if (DBG) Log.d(TAG, "registerApp()");
735 if (mService == null) return false;
736
737 mCallback = callback;
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700738 mHandler = handler;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800739 UUID uuid = UUID.randomUUID();
740 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
741
742 try {
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700743 mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800744 } catch (RemoteException e) {
745 Log.e(TAG,"",e);
746 return false;
747 }
748
749 return true;
750 }
751
752 /**
753 * Unregister the current application and callbacks.
754 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800755 private void unregisterApp() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800756 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
757 if (mService == null || mClientIf == 0) return;
758
759 try {
760 mCallback = null;
761 mService.unregisterClient(mClientIf);
762 mClientIf = 0;
763 } catch (RemoteException e) {
764 Log.e(TAG,"",e);
765 }
766 }
767
768 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800769 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800770 *
771 * <p>The connection may not be established right away, but will be
772 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700773 * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800774 * invoked when the connection state changes as a result of this function.
775 *
Andre Eisenbach6ce4db02014-07-16 23:02:42 -0700776 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800777 * the remote device, or rather passively scan and finalize the connection
778 * when the remote device is in range/available. Generally, the first ever
779 * connection to a device should be direct (autoConnect set to false) and
780 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800781 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800782 *
783 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
784 *
785 * @param device Remote device to connect to
786 * @param autoConnect Whether to directly connect to the remote device (false)
787 * or to automatically connect as soon as the remote
788 * device becomes available (true).
789 * @return true, if the connection attempt was initiated successfully
790 */
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700791 /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
792 Handler handler) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800793 if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
794 synchronized(mStateLock) {
795 if (mConnState != CONN_STATE_IDLE) {
796 throw new IllegalStateException("Not idle");
797 }
798 mConnState = CONN_STATE_CONNECTING;
799 }
Sungki Kimd35167a2016-05-19 10:18:07 -0700800
801 mAutoConnect = autoConnect;
802
Jakub Pawlowskib0f64742017-04-21 03:49:00 -0700803 if (!registerApp(callback, handler)) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800804 synchronized(mStateLock) {
805 mConnState = CONN_STATE_IDLE;
806 }
807 Log.e(TAG, "Failed to register callback");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800808 return false;
809 }
810
Sungki Kimd35167a2016-05-19 10:18:07 -0700811 // The connection will continue in the onClientRegistered callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800812 return true;
813 }
814
815 /**
816 * Disconnects an established connection, or cancels a connection attempt
817 * currently in progress.
818 *
819 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800820 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800821 public void disconnect() {
822 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800823 if (mService == null || mClientIf == 0) return;
824
825 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800826 mService.clientDisconnect(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800827 } catch (RemoteException e) {
828 Log.e(TAG,"",e);
829 }
Matthew Xie33ec9842013-04-03 00:29:27 -0700830 }
831
832 /**
833 * Connect back to remote device.
834 *
835 * <p>This method is used to re-connect to a remote device after the
836 * connection has been dropped. If the device is not in range, the
837 * re-connection will be triggered once the device is back in range.
838 *
839 * @return true, if the connection attempt was initiated successfully
840 */
841 public boolean connect() {
842 try {
843 mService.clientConnect(mClientIf, mDevice.getAddress(),
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800844 false, mTransport, mPhy); // autoConnect is inverse of "isDirect"
Matthew Xie33ec9842013-04-03 00:29:27 -0700845 return true;
846 } catch (RemoteException e) {
847 Log.e(TAG,"",e);
848 return false;
849 }
850 }
851
852 /**
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800853 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowskif752ace2017-03-22 22:44:09 -0700854 * recommendation, whether the PHY change will happen depends on other applications peferences,
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800855 * local and remote controller capabilities. Controller can override these settings.
856 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700857 * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800858 * if no PHY change happens. It is also triggered when remote device updates the PHY.
859 *
860 * @param txPhy preferred transmitter PHY. Bitwise OR of any of
Jakub Pawlowskia5151372017-04-12 08:51:22 -0700861 * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK},
862 * and {@link BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800863 * @param rxPhy preferred receiver PHY. Bitwise OR of any of
Jakub Pawlowskia5151372017-04-12 08:51:22 -0700864 * {@link BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK},
865 * and {@link BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800866 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
867 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
868 * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
869 */
870 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
871 try {
872 mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
873 phyOptions);
874 } catch (RemoteException e) {
875 Log.e(TAG,"",e);
876 }
877 }
878
879 /**
880 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700881 * in {@link BluetoothGattCallback#onPhyRead}
Jakub Pawlowskidb5a87d2017-02-02 08:07:12 -0800882 */
883 public void readPhy() {
884 try {
885 mService.clientReadPhy(mClientIf, mDevice.getAddress());
886 } catch (RemoteException e) {
887 Log.e(TAG,"",e);
888 }
889 }
890
891 /**
Matthew Xie33ec9842013-04-03 00:29:27 -0700892 * Return the remote bluetooth device this GATT client targets to
893 *
894 * @return remote bluetooth device
895 */
896 public BluetoothDevice getDevice() {
897 return mDevice;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800898 }
899
900 /**
901 * Discovers services offered by a remote device as well as their
902 * characteristics and descriptors.
903 *
904 * <p>This is an asynchronous operation. Once service discovery is completed,
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700905 * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800906 * triggered. If the discovery was successful, the remote services can be
907 * retrieved using the {@link #getServices} function.
908 *
909 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
910 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800911 * @return true, if the remote service discovery has been started
912 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800913 public boolean discoverServices() {
914 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800915 if (mService == null || mClientIf == 0) return false;
916
917 mServices.clear();
918
919 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800920 mService.discoverServices(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800921 } catch (RemoteException e) {
922 Log.e(TAG,"",e);
923 return false;
924 }
925
926 return true;
927 }
928
929 /**
930 * Returns a list of GATT services offered by the remote device.
931 *
932 * <p>This function requires that service discovery has been completed
933 * for the given device.
934 *
935 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
936 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800937 * @return List of services on the remote device. Returns an empty list
938 * if service discovery has not yet been performed.
939 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800940 public List<BluetoothGattService> getServices() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800941 List<BluetoothGattService> result =
942 new ArrayList<BluetoothGattService>();
943
944 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800945 if (service.getDevice().equals(mDevice)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800946 result.add(service);
947 }
948 }
949
950 return result;
951 }
952
953 /**
954 * Returns a {@link BluetoothGattService}, if the requested UUID is
955 * supported by the remote device.
956 *
957 * <p>This function requires that service discovery has been completed
958 * for the given device.
959 *
960 * <p>If multiple instances of the same service (as identified by UUID)
961 * exist, the first instance of the service is returned.
962 *
963 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
964 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800965 * @param uuid UUID of the requested service
966 * @return BluetoothGattService if supported, or null if the requested
967 * service is not offered by the remote device.
968 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800969 public BluetoothGattService getService(UUID uuid) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800970 for (BluetoothGattService service : mServices) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800971 if (service.getDevice().equals(mDevice) &&
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800972 service.getUuid().equals(uuid)) {
973 return service;
974 }
975 }
976
977 return null;
978 }
979
980 /**
981 * Reads the requested characteristic from the associated remote device.
982 *
983 * <p>This is an asynchronous operation. The result of the read operation
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700984 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800985 * callback.
986 *
987 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
988 *
989 * @param characteristic Characteristic to read from the remote device
990 * @return true, if the read operation was initiated successfully
991 */
992 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
993 if ((characteristic.getProperties() &
994 BluetoothGattCharacteristic.PROPERTY_READ) == 0) return false;
995
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700996 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800997 if (mService == null || mClientIf == 0) return false;
998
999 BluetoothGattService service = characteristic.getService();
1000 if (service == null) return false;
1001
1002 BluetoothDevice device = service.getDevice();
1003 if (device == null) return false;
1004
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001005 synchronized(mDeviceBusy) {
1006 if (mDeviceBusy) return false;
1007 mDeviceBusy = true;
1008 }
1009
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001010 try {
1011 mService.readCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001012 characteristic.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001013 } catch (RemoteException e) {
1014 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001015 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001016 return false;
1017 }
1018
1019 return true;
1020 }
1021
1022 /**
Jakub Pawlowskice21cb92017-04-14 07:21:20 -07001023 * Reads the characteristic using its UUID from the associated remote device.
1024 *
1025 * <p>This is an asynchronous operation. The result of the read operation
1026 * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
1027 * callback.
1028 *
1029 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1030 *
1031 * @param uuid UUID of characteristic to read from the remote device
1032 * @return true, if the read operation was initiated successfully
1033 * @hide
1034 */
1035 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
1036 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
1037 if (mService == null || mClientIf == 0) return false;
1038
1039 synchronized(mDeviceBusy) {
1040 if (mDeviceBusy) return false;
1041 mDeviceBusy = true;
1042 }
1043
1044 try {
1045 mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(),
1046 new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE);
1047 } catch (RemoteException e) {
1048 Log.e(TAG,"",e);
1049 mDeviceBusy = false;
1050 return false;
1051 }
1052
1053 return true;
1054 }
1055
1056
1057 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001058 * Writes a given characteristic and its values to the associated remote device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001059 *
1060 * <p>Once the write operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001061 * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001062 * reporting the result of the operation.
1063 *
1064 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1065 *
1066 * @param characteristic Characteristic to write on the remote device
1067 * @return true, if the write operation was initiated successfully
1068 */
1069 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1070 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
1071 && (characteristic.getProperties() &
1072 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) return false;
1073
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001074 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001075 if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001076
1077 BluetoothGattService service = characteristic.getService();
1078 if (service == null) return false;
1079
1080 BluetoothDevice device = service.getDevice();
1081 if (device == null) return false;
1082
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001083 synchronized(mDeviceBusy) {
1084 if (mDeviceBusy) return false;
1085 mDeviceBusy = true;
1086 }
1087
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001088 try {
1089 mService.writeCharacteristic(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001090 characteristic.getInstanceId(), characteristic.getWriteType(),
1091 AUTHENTICATION_NONE, characteristic.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001092 } catch (RemoteException e) {
1093 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001094 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001095 return false;
1096 }
1097
1098 return true;
1099 }
1100
1101 /**
1102 * Reads the value for a given descriptor from the associated remote device.
1103 *
1104 * <p>Once the read operation has been completed, the
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001105 * {@link BluetoothGattCallback#onDescriptorRead} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001106 * triggered, signaling the result of the operation.
1107 *
1108 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1109 *
1110 * @param descriptor Descriptor value to read from the remote device
1111 * @return true, if the read operation was initiated successfully
1112 */
1113 public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001114 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001115 if (mService == null || mClientIf == 0) return false;
1116
1117 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1118 if (characteristic == null) return false;
1119
1120 BluetoothGattService service = characteristic.getService();
1121 if (service == null) return false;
1122
1123 BluetoothDevice device = service.getDevice();
1124 if (device == null) return false;
1125
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001126 synchronized(mDeviceBusy) {
1127 if (mDeviceBusy) return false;
1128 mDeviceBusy = true;
1129 }
1130
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001131 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001132 mService.readDescriptor(mClientIf, device.getAddress(),
1133 descriptor.getInstanceId(), AUTHENTICATION_NONE);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001134 } catch (RemoteException e) {
1135 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001136 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001137 return false;
1138 }
1139
1140 return true;
1141 }
1142
1143 /**
1144 * Write the value of a given descriptor to the associated remote device.
1145 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001146 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001147 * triggered to report the result of the write operation.
1148 *
1149 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1150 *
1151 * @param descriptor Descriptor to write to the associated remote device
1152 * @return true, if the write operation was initiated successfully
1153 */
1154 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001155 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
Prerepa Viswanadhamff5e5db32014-12-04 10:12:55 -08001156 if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001157
1158 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
1159 if (characteristic == null) return false;
1160
1161 BluetoothGattService service = characteristic.getService();
1162 if (service == null) return false;
1163
1164 BluetoothDevice device = service.getDevice();
1165 if (device == null) return false;
1166
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001167 synchronized(mDeviceBusy) {
1168 if (mDeviceBusy) return false;
1169 mDeviceBusy = true;
1170 }
1171
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001172 try {
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001173 mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
Jakub Pawlowski8e970d62016-03-30 22:58:17 -07001174 AUTHENTICATION_NONE, descriptor.getValue());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001175 } catch (RemoteException e) {
1176 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001177 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001178 return false;
1179 }
1180
1181 return true;
1182 }
1183
1184 /**
1185 * Initiates a reliable write transaction for a given remote device.
1186 *
1187 * <p>Once a reliable write transaction has been initiated, all calls
1188 * to {@link #writeCharacteristic} are sent to the remote device for
1189 * verification and queued up for atomic execution. The application will
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001190 * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001191 * in response to every {@link #writeCharacteristic} call and is responsible
1192 * for verifying if the value has been transmitted accurately.
1193 *
1194 * <p>After all characteristics have been queued up and verified,
1195 * {@link #executeReliableWrite} will execute all writes. If a characteristic
1196 * was not written correctly, calling {@link #abortReliableWrite} will
1197 * cancel the current transaction without commiting any values on the
1198 * remote device.
1199 *
1200 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1201 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001202 * @return true, if the reliable write transaction has been initiated
1203 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001204 public boolean beginReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001205 if (VDBG) Log.d(TAG, "beginReliableWrite() - 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.beginReliableWrite(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 /**
1219 * Executes a reliable write transaction for a given remote device.
1220 *
1221 * <p>This function will commit all queued up characteristic write
1222 * operations for a given remote device.
1223 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001224 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001225 * invoked to indicate whether the transaction has been executed correctly.
1226 *
1227 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1228 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001229 * @return true, if the request to execute the transaction has been sent
1230 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001231 public boolean executeReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001232 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001233 if (mService == null || mClientIf == 0) return false;
1234
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001235 synchronized(mDeviceBusy) {
1236 if (mDeviceBusy) return false;
1237 mDeviceBusy = true;
1238 }
1239
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001240 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001241 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001242 } catch (RemoteException e) {
1243 Log.e(TAG,"",e);
Andre Eisenbachcc68cc92014-03-18 14:26:51 -07001244 mDeviceBusy = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001245 return false;
1246 }
1247
1248 return true;
1249 }
1250
1251 /**
1252 * Cancels a reliable write transaction for a given device.
1253 *
1254 * <p>Calling this function will discard all queued characteristic write
1255 * operations for a given remote device.
1256 *
1257 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001258 */
John Du48f8b5d2013-08-19 12:20:37 -07001259 public void abortReliableWrite() {
Andre Eisenbach55d19e42014-07-18 14:38:36 -07001260 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001261 if (mService == null || mClientIf == 0) return;
1262
1263 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001264 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001265 } catch (RemoteException e) {
1266 Log.e(TAG,"",e);
1267 }
1268 }
1269
1270 /**
John Dub7b7d7a2013-08-20 14:03:28 -07001271 * @deprecated Use {@link #abortReliableWrite()}
John Du48f8b5d2013-08-19 12:20:37 -07001272 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -07001273 @Deprecated
John Du48f8b5d2013-08-19 12:20:37 -07001274 public void abortReliableWrite(BluetoothDevice mDevice) {
1275 abortReliableWrite();
1276 }
1277
1278 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001279 * Enable or disable notifications/indications for a given characteristic.
1280 *
1281 * <p>Once notifications are enabled for a characteristic, a
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001282 * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001283 * triggered if the remote device indicates that the given characteristic
1284 * has changed.
1285 *
1286 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1287 *
1288 * @param characteristic The characteristic for which to enable notifications
1289 * @param enable Set to true to enable notifications/indications
1290 * @return true, if the requested notification status was set successfully
1291 */
1292 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
1293 boolean enable) {
1294 if (DBG) Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid()
1295 + " enable: " + enable);
1296 if (mService == null || mClientIf == 0) return false;
1297
1298 BluetoothGattService service = characteristic.getService();
1299 if (service == null) return false;
1300
1301 BluetoothDevice device = service.getDevice();
1302 if (device == null) return false;
1303
1304 try {
1305 mService.registerForNotification(mClientIf, device.getAddress(),
Jakub Pawlowskic9d13c32016-03-17 16:00:16 -07001306 characteristic.getInstanceId(), enable);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001307 } catch (RemoteException e) {
1308 Log.e(TAG,"",e);
1309 return false;
1310 }
1311
1312 return true;
1313 }
1314
1315 /**
1316 * Clears the internal cache and forces a refresh of the services from the
1317 * remote device.
1318 * @hide
1319 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001320 public boolean refresh() {
1321 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001322 if (mService == null || mClientIf == 0) return false;
1323
1324 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001325 mService.refreshDevice(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001326 } catch (RemoteException e) {
1327 Log.e(TAG,"",e);
1328 return false;
1329 }
1330
1331 return true;
1332 }
1333
1334 /**
1335 * Read the RSSI for a connected remote device.
1336 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001337 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001338 * invoked when the RSSI value has been read.
1339 *
1340 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1341 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001342 * @return true, if the RSSI value has been requested successfully
1343 */
Matthew Xieddf7e472013-03-01 18:41:02 -08001344 public boolean readRemoteRssi() {
1345 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001346 if (mService == null || mClientIf == 0) return false;
1347
1348 try {
Matthew Xieddf7e472013-03-01 18:41:02 -08001349 mService.readRemoteRssi(mClientIf, mDevice.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001350 } catch (RemoteException e) {
1351 Log.e(TAG,"",e);
1352 return false;
1353 }
1354
1355 return true;
1356 }
1357
1358 /**
Andre Eisenbach4072da02014-08-19 17:58:55 -07001359 * Request an MTU size used for a given connection.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001360 *
1361 * <p>When performing a write request operation (write without response),
1362 * the data sent is truncated to the MTU size. This function may be used
Andre Eisenbach4072da02014-08-19 17:58:55 -07001363 * to request a larger MTU size to be able to send more data at once.
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001364 *
Jakub Pawlowskid64bb882017-03-22 11:22:18 -07001365 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001366 * whether this operation was successful.
1367 *
1368 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
1369 *
1370 * @return true, if the new MTU value has been requested successfully
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001371 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001372 public boolean requestMtu(int mtu) {
Andre Eisenbach580b0a12014-03-25 06:31:50 -07001373 if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
1374 + " mtu: " + mtu);
1375 if (mService == null || mClientIf == 0) return false;
1376
1377 try {
1378 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu);
1379 } catch (RemoteException e) {
1380 Log.e(TAG,"",e);
1381 return false;
1382 }
1383
1384 return true;
1385 }
1386
1387 /**
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001388 * Request a connection parameter update.
1389 *
1390 * <p>This function will send a connection parameter update request to the
1391 * remote device.
1392 *
1393 * @param connectionPriority Request a specific connection priority. Must be one of
Andre Eisenbach4072da02014-08-19 17:58:55 -07001394 * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
1395 * {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH}
1396 * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001397 * @throws IllegalArgumentException If the parameters are outside of their
1398 * specified range.
1399 */
Andre Eisenbach4072da02014-08-19 17:58:55 -07001400 public boolean requestConnectionPriority(int connectionPriority) {
1401 if (connectionPriority < CONNECTION_PRIORITY_BALANCED ||
1402 connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001403 throw new IllegalArgumentException("connectionPriority not within valid range");
1404 }
1405
Andre Eisenbach4072da02014-08-19 17:58:55 -07001406 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority);
Andre Eisenbach6ce4db02014-07-16 23:02:42 -07001407 if (mService == null || mClientIf == 0) return false;
1408
1409 try {
1410 mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority);
1411 } catch (RemoteException e) {
1412 Log.e(TAG,"",e);
1413 return false;
1414 }
1415
1416 return true;
1417 }
1418
1419 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001420 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1421 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001422 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001423 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001424 */
1425 @Override
1426 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001427 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001428 }
1429
1430 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001431 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
1432 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001433 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001434 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001435 */
1436 @Override
1437 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -08001438 throw new UnsupportedOperationException
1439 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001440 }
1441
1442 /**
Matthew Xieddf7e472013-03-01 18:41:02 -08001443 * Not supported - please use
1444 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
1445 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001446 *
Matthew Xieddf7e472013-03-01 18:41:02 -08001447 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001448 */
1449 @Override
1450 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -08001451 throw new UnsupportedOperationException
1452 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001453 }
1454}