blob: b35a59335939936d517e6617ff776a7cff0390dc [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
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothProfile;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080022import android.content.Context;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080023import android.os.ParcelUuid;
24import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080025import android.util.Log;
26
27import java.util.ArrayList;
28import java.util.List;
29import java.util.UUID;
30
31/**
Matthew Xieddf7e472013-03-01 18:41:02 -080032 * Public API for the Bluetooth GATT Profile server role.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080033 *
Matthew Xieddf7e472013-03-01 18:41:02 -080034 * <p>This class provides Bluetooth GATT server role functionality,
Andre Eisenbach061cfd02014-06-27 14:31:37 -070035 * allowing applications to create Bluetooth Smart services and
36 * characteristics.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080037 *
38 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
Andre Eisenbach061cfd02014-06-27 14:31:37 -070039 * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance
40 * of this class.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080041 */
42public final class BluetoothGattServer implements BluetoothProfile {
43 private static final String TAG = "BluetoothGattServer";
44 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070045 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080046
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080047 private BluetoothAdapter mAdapter;
48 private IBluetoothGatt mService;
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070049 private BluetoothGattServerCallback mCallback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080050
Matthew Xieddf7e472013-03-01 18:41:02 -080051 private Object mServerIfLock = new Object();
52 private int mServerIf;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070053 private int mTransport;
Jakub Pawlowskid75f5122016-04-01 07:51:45 -070054 private BluetoothGattService mPendingService;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080055 private List<BluetoothGattService> mServices;
56
Matthew Xieddf7e472013-03-01 18:41:02 -080057 private static final int CALLBACK_REG_TIMEOUT = 10000;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080058
59 /**
60 * Bluetooth GATT interface callbacks
61 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -070062 private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
63 new IBluetoothGattServerCallback.Stub() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080064 /**
65 * Application interface registered - app is ready to go
66 * @hide
67 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -070068 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080069 public void onServerRegistered(int status, int serverIf) {
70 if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
71 + " serverIf=" + serverIf);
Matthew Xieddf7e472013-03-01 18:41:02 -080072 synchronized(mServerIfLock) {
73 if (mCallback != null) {
74 mServerIf = serverIf;
75 mServerIfLock.notify();
76 } else {
77 // registration timeout
78 Log.e(TAG, "onServerRegistered: mCallback is null");
79 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080080 }
81 }
82
83 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080084 * Server connection state changed
85 * @hide
86 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -070087 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080088 public void onServerConnectionState(int status, int serverIf,
89 boolean connected, String address) {
90 if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status
91 + " serverIf=" + serverIf + " device=" + address);
92 try {
93 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
94 connected ? BluetoothProfile.STATE_CONNECTED :
95 BluetoothProfile.STATE_DISCONNECTED);
96 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -070097 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080098 }
99 }
100
101 /**
102 * Service has been added
103 * @hide
104 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700105 @Override
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700106 public void onServiceAdded(int status, BluetoothGattService service) {
107 if (DBG) Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId()
108 + " uuid=" + service.getUuid() + " status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800109
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700110 if (mPendingService == null)
111 return;
112
113 BluetoothGattService tmp = mPendingService;
114 mPendingService = null;
115
116 // Rewrite newly assigned handles to existing service.
117 tmp.setInstanceId(service.getInstanceId());
118 List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics();
119 List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics();
120 for (int i=0; i<svc_chars.size(); i++) {
121 BluetoothGattCharacteristic temp_char = temp_chars.get(i);
122 BluetoothGattCharacteristic svc_char = svc_chars.get(i);
123
124 temp_char.setInstanceId(svc_char.getInstanceId());
125
126 List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors();
127 List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors();
128 for (int j=0; j<svc_descs.size(); j++) {
Jakub Pawlowskid167db12016-08-04 13:16:32 -0700129 temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId());
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700130 }
131 }
132
133 mServices.add(tmp);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800134
135 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700136 mCallback.onServiceAdded((int)status, tmp);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700138 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800139 }
140 }
141
142 /**
143 * Remote client characteristic read request.
144 * @hide
145 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700146 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800147 public void onCharacteristicReadRequest(String address, int transId,
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700148 int offset, boolean isLong, int handle) {
149 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800150
151 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700152 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
153 if (characteristic == null) {
154 Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle);
155 return;
156 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800157
158 try {
159 mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
160 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700161 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800162 }
163 }
164
165 /**
166 * Remote client descriptor read request.
167 * @hide
168 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700169 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800170 public void onDescriptorReadRequest(String address, int transId,
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700171 int offset, boolean isLong, int handle) {
172 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800173
174 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700175 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
176 if (descriptor == null) {
177 Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle);
178 return;
179 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800180
181 try {
182 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
183 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700184 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800185 }
186 }
187
188 /**
189 * Remote client characteristic write request.
190 * @hide
191 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700192 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800193 public void onCharacteristicWriteRequest(String address, int transId,
194 int offset, int length, boolean isPrep, boolean needRsp,
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700195 int handle, byte[] value) {
196 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800197
198 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700199 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
200 if (characteristic == null) {
201 Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle);
202 return;
203 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800204
205 try {
206 mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
207 isPrep, needRsp, offset, value);
208 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700209 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800210 }
211
212 }
213
214 /**
215 * Remote client descriptor write request.
216 * @hide
217 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700218 @Override
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700219 public void onDescriptorWriteRequest(String address, int transId, int offset,
220 int length, boolean isPrep, boolean needRsp, int handle, byte[] value) {
221 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800222
223 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700224 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
225 if (descriptor == null) {
226 Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle);
227 return;
228 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800229
230 try {
231 mCallback.onDescriptorWriteRequest(device, transId, descriptor,
232 isPrep, needRsp, offset, value);
233 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700234 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800235 }
236 }
237
238 /**
239 * Execute pending writes.
240 * @hide
241 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700242 @Override
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800243 public void onExecuteWrite(String address, int transId,
244 boolean execWrite) {
245 if (DBG) Log.d(TAG, "onExecuteWrite() - "
246 + "device=" + address + ", transId=" + transId
247 + "execWrite=" + execWrite);
248
249 BluetoothDevice device = mAdapter.getRemoteDevice(address);
250 if (device == null) return;
251
252 try {
253 mCallback.onExecuteWrite(device, transId, execWrite);
254 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700255 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800256 }
257 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700258
259 /**
260 * A notification/indication has been sent.
261 * @hide
262 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700263 @Override
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700264 public void onNotificationSent(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700265 if (VDBG) Log.d(TAG, "onNotificationSent() - "
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700266 + "device=" + address + ", status=" + status);
267
268 BluetoothDevice device = mAdapter.getRemoteDevice(address);
269 if (device == null) return;
270
271 try {
272 mCallback.onNotificationSent(device, status);
273 } catch (Exception ex) {
274 Log.w(TAG, "Unhandled exception: " + ex);
275 }
276 }
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800277
278 /**
279 * The MTU for a connection has changed
280 * @hide
281 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700282 @Override
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800283 public void onMtuChanged(String address, int mtu) {
284 if (DBG) Log.d(TAG, "onMtuChanged() - "
285 + "device=" + address + ", mtu=" + mtu);
286
287 BluetoothDevice device = mAdapter.getRemoteDevice(address);
288 if (device == null) return;
289
290 try {
291 mCallback.onMtuChanged(device, mtu);
292 } catch (Exception ex) {
293 Log.w(TAG, "Unhandled exception: " + ex);
294 }
295 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800296
297 /**
298 * The PHY for a connection was updated
299 * @hide
300 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700301 @Override
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800302 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
303 if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
304 + ", rxPHy=" + rxPhy);
305
306 BluetoothDevice device = mAdapter.getRemoteDevice(address);
307 if (device == null) return;
308
309 try {
310 mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
311 } catch (Exception ex) {
312 Log.w(TAG, "Unhandled exception: " + ex);
313 }
314 }
315
316 /**
317 * The PHY for a connection was read
318 * @hide
319 */
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700320 @Override
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800321 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
322 if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
323 + ", rxPHy=" + rxPhy);
324
325 BluetoothDevice device = mAdapter.getRemoteDevice(address);
326 if (device == null) return;
327
328 try {
329 mCallback.onPhyRead(device, txPhy, rxPhy, status);
330 } catch (Exception ex) {
331 Log.w(TAG, "Unhandled exception: " + ex);
332 }
333 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700334
335 /**
336 * Callback invoked when the given connection is updated
337 * @hide
338 */
339 @Override
340 public void onConnectionUpdated(String address, int interval, int latency,
341 int timeout, int status) {
342 if (DBG) Log.d(TAG, "onConnectionUpdated() - Device=" + address +
343 " interval=" + interval + " latency=" + latency +
344 " timeout=" + timeout + " status=" + status);
345 BluetoothDevice device = mAdapter.getRemoteDevice(address);
346 if (device == null) return;
347
348 try {
349 mCallback.onConnectionUpdated(device, interval, latency,
350 timeout, status);
351 } catch (Exception ex) {
352 Log.w(TAG, "Unhandled exception: " + ex);
353 }
354 }
355
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800356 };
357
358 /**
359 * Create a BluetoothGattServer proxy object.
360 */
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700361 /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800362 mService = iGatt;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800363 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xieddf7e472013-03-01 18:41:02 -0800364 mCallback = null;
365 mServerIf = 0;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700366 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800367 mServices = new ArrayList<BluetoothGattService>();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800368 }
369
370 /**
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700371 * Returns a characteristic with given handle.
372 * @hide
373 */
374 /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
375 for(BluetoothGattService svc : mServices) {
376 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
377 if (charac.getInstanceId() == handle)
378 return charac;
379 }
380 }
381 return null;
382 }
383
384 /**
385 * Returns a descriptor with given handle.
386 * @hide
387 */
388 /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
389 for(BluetoothGattService svc : mServices) {
390 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
391 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
392 if (desc.getInstanceId() == handle)
393 return desc;
394 }
395 }
396 }
397 return null;
398 }
399
400 /**
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700401 * Close this GATT server instance.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700402 *
403 * Application should call this method as early as possible after it is done with
404 * this GATT server.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800405 */
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700406 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800407 if (DBG) Log.d(TAG, "close()");
Matthew Xieddf7e472013-03-01 18:41:02 -0800408 unregisterCallback();
409 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800410
Matthew Xieddf7e472013-03-01 18:41:02 -0800411 /**
412 * Register an application callback to start using GattServer.
413 *
414 * <p>This is an asynchronous call. The callback is used to notify
415 * success or failure if the function returns true.
416 *
417 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
418 *
419 * @param callback GATT callback handler that will receive asynchronous
420 * callbacks.
421 * @return true, the callback will be called to notify success or failure,
422 * false on immediate error
423 */
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700424 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800425 if (DBG) Log.d(TAG, "registerCallback()");
426 if (mService == null) {
427 Log.e(TAG, "GATT service not available");
428 return false;
429 }
430 UUID uuid = UUID.randomUUID();
431 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800432
Matthew Xieddf7e472013-03-01 18:41:02 -0800433 synchronized(mServerIfLock) {
434 if (mCallback != null) {
435 Log.e(TAG, "App can register callback only once");
436 return false;
437 }
438
439 mCallback = callback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800441 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
442 } catch (RemoteException e) {
443 Log.e(TAG,"",e);
444 mCallback = null;
445 return false;
446 }
447
448 try {
449 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
450 } catch (InterruptedException e) {
451 Log.e(TAG, "" + e);
452 mCallback = null;
453 }
454
455 if (mServerIf == 0) {
456 mCallback = null;
457 return false;
458 } else {
459 return true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800460 }
461 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800462 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800463
Matthew Xieddf7e472013-03-01 18:41:02 -0800464 /**
465 * Unregister the current application and callbacks.
466 */
467 private void unregisterCallback() {
468 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
469 if (mService == null || mServerIf == 0) return;
470
471 try {
472 mCallback = null;
473 mService.unregisterServer(mServerIf);
474 mServerIf = 0;
475 } catch (RemoteException e) {
476 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800477 }
478 }
479
480 /**
481 * Returns a service by UUID, instance and type.
482 * @hide
483 */
484 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
485 for(BluetoothGattService svc : mServices) {
486 if (svc.getType() == type &&
487 svc.getInstanceId() == instanceId &&
488 svc.getUuid().equals(uuid)) {
489 return svc;
490 }
491 }
492 return null;
493 }
494
495 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800496 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800497 *
498 * <p>The connection may not be established right away, but will be
499 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700500 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800501 * invoked when the connection state changes as a result of this function.
502 *
503 * <p>The autoConnect paramter determines whether to actively connect to
504 * the remote device, or rather passively scan and finalize the connection
505 * when the remote device is in range/available. Generally, the first ever
506 * connection to a device should be direct (autoConnect set to false) and
507 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800508 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800509 *
510 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
511 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800512 * @param autoConnect Whether to directly connect to the remote device (false)
513 * or to automatically connect as soon as the remote
514 * device becomes available (true).
515 * @return true, if the connection attempt was initiated successfully
516 */
517 public boolean connect(BluetoothDevice device, boolean autoConnect) {
Andre Eisenbach3f366602013-03-08 18:42:24 -0800518 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 if (mService == null || mServerIf == 0) return false;
520
521 try {
522 mService.serverConnect(mServerIf, device.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700523 autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect"
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800524 } catch (RemoteException e) {
525 Log.e(TAG,"",e);
526 return false;
527 }
528
529 return true;
530 }
531
532 /**
533 * Disconnects an established connection, or cancels a connection attempt
534 * currently in progress.
535 *
536 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
537 *
538 * @param device Remote device
539 */
540 public void cancelConnection(BluetoothDevice device) {
541 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
542 if (mService == null || mServerIf == 0) return;
543
544 try {
545 mService.serverDisconnect(mServerIf, device.getAddress());
546 } catch (RemoteException e) {
547 Log.e(TAG,"",e);
548 }
549 }
550
551 /**
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800552 * Set the preferred connection PHY for this app. Please note that this is just a
553 * recommendation, wether the PHY change will happen depends on other applications peferences,
554 * local and remote controller capabilities. Controller can override these settings.
555 * <p>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700556 * {@link BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800557 * if no PHY change happens. It is also triggered when remote device updates the PHY.
558 *
559 * @param device The remote device to send this response to
560 * @param txPhy preferred transmitter PHY. Bitwise OR of any of
561 * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
562 * {@link BluetoothDevice#PHY_LE_CODED}.
563 * @param rxPhy preferred receiver PHY. Bitwise OR of any of
564 * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
565 * {@link BluetoothDevice#PHY_LE_CODED}.
566 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
567 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
568 * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
569 */
570 public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
571 try {
572 mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
573 phyOptions);
574 } catch (RemoteException e) {
575 Log.e(TAG,"",e);
576 }
577 }
578
579 /**
580 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700581 * in {@link BluetoothGattServerCallback#onPhyRead}
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800582 *
583 * @param device The remote device to send this response to
584 */
585 public void readPhy(BluetoothDevice device) {
586 try {
587 mService.serverReadPhy(mServerIf, device.getAddress());
588 } catch (RemoteException e) {
589 Log.e(TAG,"",e);
590 }
591 }
592
593 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800594 * Send a response to a read or write request to a remote device.
595 *
596 * <p>This function must be invoked in when a remote read/write request
Matthew Xieddf7e472013-03-01 18:41:02 -0800597 * is received by one of these callback methods:
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800598 *
599 * <ul>
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700600 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
601 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
602 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
603 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800604 * </ul>
605 *
606 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
607 *
608 * @param device The remote device to send this response to
609 * @param requestId The ID of the request that was received with the callback
610 * @param status The status of the request to be sent to the remote devices
611 * @param offset Value offset for partial read/write response
612 * @param value The value of the attribute that was read/written (optional)
613 */
614 public boolean sendResponse(BluetoothDevice device, int requestId,
615 int status, int offset, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700616 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800617 if (mService == null || mServerIf == 0) return false;
618
619 try {
620 mService.sendResponse(mServerIf, device.getAddress(), requestId,
621 status, offset, value);
622 } catch (RemoteException e) {
623 Log.e(TAG,"",e);
624 return false;
625 }
626 return true;
627 }
628
629 /**
630 * Send a notification or indication that a local characteristic has been
631 * updated.
632 *
633 * <p>A notification or indication is sent to the remote device to signal
634 * that the characteristic has been updated. This function should be invoked
635 * for every client that requests notifications/indications by writing
636 * to the "Client Configuration" descriptor for the given characteristic.
637 *
638 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
639 *
640 * @param device The remote device to receive the notification/indication
641 * @param characteristic The local characteristic that has been updated
642 * @param confirm true to request confirmation from the client (indication),
643 * false to send a notification
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700644 * @throws IllegalArgumentException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800645 * @return true, if the notification has been triggered successfully
646 */
647 public boolean notifyCharacteristicChanged(BluetoothDevice device,
648 BluetoothGattCharacteristic characteristic, boolean confirm) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700649 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800650 if (mService == null || mServerIf == 0) return false;
651
652 BluetoothGattService service = characteristic.getService();
653 if (service == null) return false;
654
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700655 if (characteristic.getValue() == null) {
656 throw new IllegalArgumentException("Chracteristic value is empty. Use "
657 + "BluetoothGattCharacteristic#setvalue to update");
658 }
659
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800660 try {
661 mService.sendNotification(mServerIf, device.getAddress(),
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700662 characteristic.getInstanceId(), confirm,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800663 characteristic.getValue());
664 } catch (RemoteException e) {
665 Log.e(TAG,"",e);
666 return false;
667 }
668
669 return true;
670 }
671
672 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800673 * Add a service to the list of services to be hosted.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800674 *
Andre Eisenbach061cfd02014-06-27 14:31:37 -0700675 * <p>Once a service has been addded to the the list, the service and its
Matthew Xieddf7e472013-03-01 18:41:02 -0800676 * included characteristics will be provided by the local device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800677 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800678 * <p>If the local device has already exposed services when this function
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800679 * is called, a service update notification will be sent to all clients.
680 *
681 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
682 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800683 * @param service Service to be added to the list of services provided
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800684 * by this device.
685 * @return true, if the service has been added successfully
686 */
687 public boolean addService(BluetoothGattService service) {
688 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
689 if (mService == null || mServerIf == 0) return false;
690
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700691 mPendingService = service;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800692
693 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700694 mService.addService(mServerIf, service);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800695 } catch (RemoteException e) {
696 Log.e(TAG,"",e);
697 return false;
698 }
699
700 return true;
701 }
702
703 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800704 * Removes a service from the list of services to be provided.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800705 *
706 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
707 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800708 * @param service Service to be removed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800709 * @return true, if the service has been removed
710 */
711 public boolean removeService(BluetoothGattService service) {
712 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
713 if (mService == null || mServerIf == 0) return false;
714
715 BluetoothGattService intService = getService(service.getUuid(),
716 service.getInstanceId(), service.getType());
717 if (intService == null) return false;
718
719 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700720 mService.removeService(mServerIf, service.getInstanceId());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 mServices.remove(intService);
722 } catch (RemoteException e) {
723 Log.e(TAG,"",e);
724 return false;
725 }
726
727 return true;
728 }
729
730 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800731 * Remove all services from the list of provided services.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800732 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
733 */
734 public void clearServices() {
735 if (DBG) Log.d(TAG, "clearServices()");
736 if (mService == null || mServerIf == 0) return;
737
738 try {
739 mService.clearServices(mServerIf);
740 mServices.clear();
741 } catch (RemoteException e) {
742 Log.e(TAG,"",e);
743 }
744 }
745
746 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800747 * Returns a list of GATT services offered by this device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800748 *
749 * <p>An application must call {@link #addService} to add a serice to the
750 * list of services offered by this device.
751 *
752 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
753 *
754 * @return List of services. Returns an empty list
755 * if no services have been added yet.
756 */
757 public List<BluetoothGattService> getServices() {
758 return mServices;
759 }
760
761 /**
762 * Returns a {@link BluetoothGattService} from the list of services offered
763 * by this device.
764 *
765 * <p>If multiple instances of the same service (as identified by UUID)
766 * exist, the first instance of the service is returned.
767 *
768 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
769 *
770 * @param uuid UUID of the requested service
771 * @return BluetoothGattService if supported, or null if the requested
772 * service is not offered by this device.
773 */
774 public BluetoothGattService getService(UUID uuid) {
775 for (BluetoothGattService service : mServices) {
776 if (service.getUuid().equals(uuid)) {
777 return service;
778 }
779 }
780
781 return null;
782 }
783
Matthew Xieddf7e472013-03-01 18:41:02 -0800784
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800785 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800786 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
787 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800788 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800789 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800790 */
791 @Override
792 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800793 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800794 }
795
796 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800797 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
798 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800799 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800800 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800801 */
802 @Override
803 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -0800804 throw new UnsupportedOperationException
805 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800806 }
807
808 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800809 * Not supported - please use
810 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
811 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800812 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800813 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800814 */
815 @Override
816 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800817 throw new UnsupportedOperationException
818 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800819 }
820}