blob: 5ffceba5e11d7d746be35af42c609bd6e47a049b [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;
49 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 */
62 private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
63 new IBluetoothGattServerCallback.Stub() {
64 /**
65 * Application interface registered - app is ready to go
66 * @hide
67 */
68 public void onServerRegistered(int status, int serverIf) {
69 if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
70 + " serverIf=" + serverIf);
Matthew Xieddf7e472013-03-01 18:41:02 -080071 synchronized(mServerIfLock) {
72 if (mCallback != null) {
73 mServerIf = serverIf;
74 mServerIfLock.notify();
75 } else {
76 // registration timeout
77 Log.e(TAG, "onServerRegistered: mCallback is null");
78 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080079 }
80 }
81
82 /**
83 * Callback reporting an LE scan result.
84 * @hide
85 */
86 public void onScanResult(String address, int rssi, byte[] advData) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -070087 if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
Matthew Xieddf7e472013-03-01 18:41:02 -080088 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080089 }
90
91 /**
92 * Server connection state changed
93 * @hide
94 */
95 public void onServerConnectionState(int status, int serverIf,
96 boolean connected, String address) {
97 if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status
98 + " serverIf=" + serverIf + " device=" + address);
99 try {
100 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
101 connected ? BluetoothProfile.STATE_CONNECTED :
102 BluetoothProfile.STATE_DISCONNECTED);
103 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700104 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800105 }
106 }
107
108 /**
109 * Service has been added
110 * @hide
111 */
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700112 public void onServiceAdded(int status, BluetoothGattService service) {
113 if (DBG) Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId()
114 + " uuid=" + service.getUuid() + " status=" + status);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800115
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700116 if (mPendingService == null)
117 return;
118
119 BluetoothGattService tmp = mPendingService;
120 mPendingService = null;
121
122 // Rewrite newly assigned handles to existing service.
123 tmp.setInstanceId(service.getInstanceId());
124 List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics();
125 List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics();
126 for (int i=0; i<svc_chars.size(); i++) {
127 BluetoothGattCharacteristic temp_char = temp_chars.get(i);
128 BluetoothGattCharacteristic svc_char = svc_chars.get(i);
129
130 temp_char.setInstanceId(svc_char.getInstanceId());
131
132 List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors();
133 List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors();
134 for (int j=0; j<svc_descs.size(); j++) {
Jakub Pawlowskid167db12016-08-04 13:16:32 -0700135 temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId());
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700136 }
137 }
138
139 mServices.add(tmp);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800140
141 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700142 mCallback.onServiceAdded((int)status, tmp);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800143 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700144 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800145 }
146 }
147
148 /**
149 * Remote client characteristic read request.
150 * @hide
151 */
152 public void onCharacteristicReadRequest(String address, int transId,
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700153 int offset, boolean isLong, int handle) {
154 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800155
156 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700157 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
158 if (characteristic == null) {
159 Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle);
160 return;
161 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800162
163 try {
164 mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
165 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700166 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800167 }
168 }
169
170 /**
171 * Remote client descriptor read request.
172 * @hide
173 */
174 public void onDescriptorReadRequest(String address, int transId,
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700175 int offset, boolean isLong, int handle) {
176 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800177
178 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700179 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
180 if (descriptor == null) {
181 Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle);
182 return;
183 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800184
185 try {
186 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
187 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700188 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800189 }
190 }
191
192 /**
193 * Remote client characteristic write request.
194 * @hide
195 */
196 public void onCharacteristicWriteRequest(String address, int transId,
197 int offset, int length, boolean isPrep, boolean needRsp,
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700198 int handle, byte[] value) {
199 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800200
201 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700202 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
203 if (characteristic == null) {
204 Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle);
205 return;
206 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800207
208 try {
209 mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
210 isPrep, needRsp, offset, value);
211 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700212 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800213 }
214
215 }
216
217 /**
218 * Remote client descriptor write request.
219 * @hide
220 */
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700221 public void onDescriptorWriteRequest(String address, int transId, int offset,
222 int length, boolean isPrep, boolean needRsp, int handle, byte[] value) {
223 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800224
225 BluetoothDevice device = mAdapter.getRemoteDevice(address);
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700226 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
227 if (descriptor == null) {
228 Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle);
229 return;
230 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800231
232 try {
233 mCallback.onDescriptorWriteRequest(device, transId, descriptor,
234 isPrep, needRsp, offset, value);
235 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700236 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800237 }
238 }
239
240 /**
241 * Execute pending writes.
242 * @hide
243 */
244 public void onExecuteWrite(String address, int transId,
245 boolean execWrite) {
246 if (DBG) Log.d(TAG, "onExecuteWrite() - "
247 + "device=" + address + ", transId=" + transId
248 + "execWrite=" + execWrite);
249
250 BluetoothDevice device = mAdapter.getRemoteDevice(address);
251 if (device == null) return;
252
253 try {
254 mCallback.onExecuteWrite(device, transId, execWrite);
255 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700256 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800257 }
258 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700259
260 /**
261 * A notification/indication has been sent.
262 * @hide
263 */
264 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 */
282 public void onMtuChanged(String address, int mtu) {
283 if (DBG) Log.d(TAG, "onMtuChanged() - "
284 + "device=" + address + ", mtu=" + mtu);
285
286 BluetoothDevice device = mAdapter.getRemoteDevice(address);
287 if (device == null) return;
288
289 try {
290 mCallback.onMtuChanged(device, mtu);
291 } catch (Exception ex) {
292 Log.w(TAG, "Unhandled exception: " + ex);
293 }
294 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800295 };
296
297 /**
298 * Create a BluetoothGattServer proxy object.
299 */
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700300 /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800301 mService = iGatt;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800302 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xieddf7e472013-03-01 18:41:02 -0800303 mCallback = null;
304 mServerIf = 0;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700305 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800306 mServices = new ArrayList<BluetoothGattService>();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800307 }
308
309 /**
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700310 * Returns a characteristic with given handle.
311 * @hide
312 */
313 /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
314 for(BluetoothGattService svc : mServices) {
315 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
316 if (charac.getInstanceId() == handle)
317 return charac;
318 }
319 }
320 return null;
321 }
322
323 /**
324 * Returns a descriptor with given handle.
325 * @hide
326 */
327 /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
328 for(BluetoothGattService svc : mServices) {
329 for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
330 for(BluetoothGattDescriptor desc : charac.getDescriptors()) {
331 if (desc.getInstanceId() == handle)
332 return desc;
333 }
334 }
335 }
336 return null;
337 }
338
339 /**
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700340 * Close this GATT server instance.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700341 *
342 * Application should call this method as early as possible after it is done with
343 * this GATT server.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800344 */
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700345 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800346 if (DBG) Log.d(TAG, "close()");
Matthew Xieddf7e472013-03-01 18:41:02 -0800347 unregisterCallback();
348 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800349
Matthew Xieddf7e472013-03-01 18:41:02 -0800350 /**
351 * Register an application callback to start using GattServer.
352 *
353 * <p>This is an asynchronous call. The callback is used to notify
354 * success or failure if the function returns true.
355 *
356 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
357 *
358 * @param callback GATT callback handler that will receive asynchronous
359 * callbacks.
360 * @return true, the callback will be called to notify success or failure,
361 * false on immediate error
362 */
363 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
364 if (DBG) Log.d(TAG, "registerCallback()");
365 if (mService == null) {
366 Log.e(TAG, "GATT service not available");
367 return false;
368 }
369 UUID uuid = UUID.randomUUID();
370 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800371
Matthew Xieddf7e472013-03-01 18:41:02 -0800372 synchronized(mServerIfLock) {
373 if (mCallback != null) {
374 Log.e(TAG, "App can register callback only once");
375 return false;
376 }
377
378 mCallback = callback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800379 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800380 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
381 } catch (RemoteException e) {
382 Log.e(TAG,"",e);
383 mCallback = null;
384 return false;
385 }
386
387 try {
388 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
389 } catch (InterruptedException e) {
390 Log.e(TAG, "" + e);
391 mCallback = null;
392 }
393
394 if (mServerIf == 0) {
395 mCallback = null;
396 return false;
397 } else {
398 return true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800399 }
400 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800401 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800402
Matthew Xieddf7e472013-03-01 18:41:02 -0800403 /**
404 * Unregister the current application and callbacks.
405 */
406 private void unregisterCallback() {
407 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
408 if (mService == null || mServerIf == 0) return;
409
410 try {
411 mCallback = null;
412 mService.unregisterServer(mServerIf);
413 mServerIf = 0;
414 } catch (RemoteException e) {
415 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800416 }
417 }
418
419 /**
420 * Returns a service by UUID, instance and type.
421 * @hide
422 */
423 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
424 for(BluetoothGattService svc : mServices) {
425 if (svc.getType() == type &&
426 svc.getInstanceId() == instanceId &&
427 svc.getUuid().equals(uuid)) {
428 return svc;
429 }
430 }
431 return null;
432 }
433
434 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800435 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800436 *
437 * <p>The connection may not be established right away, but will be
438 * completed when the remote device is available. A
Andre Eisenbach3f366602013-03-08 18:42:24 -0800439 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800440 * invoked when the connection state changes as a result of this function.
441 *
442 * <p>The autoConnect paramter determines whether to actively connect to
443 * the remote device, or rather passively scan and finalize the connection
444 * when the remote device is in range/available. Generally, the first ever
445 * connection to a device should be direct (autoConnect set to false) and
446 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800447 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800448 *
449 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
450 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800451 * @param autoConnect Whether to directly connect to the remote device (false)
452 * or to automatically connect as soon as the remote
453 * device becomes available (true).
454 * @return true, if the connection attempt was initiated successfully
455 */
456 public boolean connect(BluetoothDevice device, boolean autoConnect) {
Andre Eisenbach3f366602013-03-08 18:42:24 -0800457 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800458 if (mService == null || mServerIf == 0) return false;
459
460 try {
461 mService.serverConnect(mServerIf, device.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700462 autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect"
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800463 } catch (RemoteException e) {
464 Log.e(TAG,"",e);
465 return false;
466 }
467
468 return true;
469 }
470
471 /**
472 * Disconnects an established connection, or cancels a connection attempt
473 * currently in progress.
474 *
475 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
476 *
477 * @param device Remote device
478 */
479 public void cancelConnection(BluetoothDevice device) {
480 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
481 if (mService == null || mServerIf == 0) return;
482
483 try {
484 mService.serverDisconnect(mServerIf, device.getAddress());
485 } catch (RemoteException e) {
486 Log.e(TAG,"",e);
487 }
488 }
489
490 /**
491 * Send a response to a read or write request to a remote device.
492 *
493 * <p>This function must be invoked in when a remote read/write request
Matthew Xieddf7e472013-03-01 18:41:02 -0800494 * is received by one of these callback methods:
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800495 *
496 * <ul>
497 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
498 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
499 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
500 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
501 * </ul>
502 *
503 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
504 *
505 * @param device The remote device to send this response to
506 * @param requestId The ID of the request that was received with the callback
507 * @param status The status of the request to be sent to the remote devices
508 * @param offset Value offset for partial read/write response
509 * @param value The value of the attribute that was read/written (optional)
510 */
511 public boolean sendResponse(BluetoothDevice device, int requestId,
512 int status, int offset, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700513 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800514 if (mService == null || mServerIf == 0) return false;
515
516 try {
517 mService.sendResponse(mServerIf, device.getAddress(), requestId,
518 status, offset, value);
519 } catch (RemoteException e) {
520 Log.e(TAG,"",e);
521 return false;
522 }
523 return true;
524 }
525
526 /**
527 * Send a notification or indication that a local characteristic has been
528 * updated.
529 *
530 * <p>A notification or indication is sent to the remote device to signal
531 * that the characteristic has been updated. This function should be invoked
532 * for every client that requests notifications/indications by writing
533 * to the "Client Configuration" descriptor for the given characteristic.
534 *
535 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
536 *
537 * @param device The remote device to receive the notification/indication
538 * @param characteristic The local characteristic that has been updated
539 * @param confirm true to request confirmation from the client (indication),
540 * false to send a notification
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700541 * @throws IllegalArgumentException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800542 * @return true, if the notification has been triggered successfully
543 */
544 public boolean notifyCharacteristicChanged(BluetoothDevice device,
545 BluetoothGattCharacteristic characteristic, boolean confirm) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700546 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800547 if (mService == null || mServerIf == 0) return false;
548
549 BluetoothGattService service = characteristic.getService();
550 if (service == null) return false;
551
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700552 if (characteristic.getValue() == null) {
553 throw new IllegalArgumentException("Chracteristic value is empty. Use "
554 + "BluetoothGattCharacteristic#setvalue to update");
555 }
556
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800557 try {
558 mService.sendNotification(mServerIf, device.getAddress(),
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700559 characteristic.getInstanceId(), confirm,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800560 characteristic.getValue());
561 } catch (RemoteException e) {
562 Log.e(TAG,"",e);
563 return false;
564 }
565
566 return true;
567 }
568
569 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800570 * Add a service to the list of services to be hosted.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800571 *
Andre Eisenbach061cfd02014-06-27 14:31:37 -0700572 * <p>Once a service has been addded to the the list, the service and its
Matthew Xieddf7e472013-03-01 18:41:02 -0800573 * included characteristics will be provided by the local device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800574 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800575 * <p>If the local device has already exposed services when this function
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800576 * is called, a service update notification will be sent to all clients.
577 *
578 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
579 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800580 * @param service Service to be added to the list of services provided
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800581 * by this device.
582 * @return true, if the service has been added successfully
583 */
584 public boolean addService(BluetoothGattService service) {
585 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
586 if (mService == null || mServerIf == 0) return false;
587
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700588 mPendingService = service;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800589
590 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700591 mService.addService(mServerIf, service);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800592 } catch (RemoteException e) {
593 Log.e(TAG,"",e);
594 return false;
595 }
596
597 return true;
598 }
599
600 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800601 * Removes a service from the list of services to be provided.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800602 *
603 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
604 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800605 * @param service Service to be removed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800606 * @return true, if the service has been removed
607 */
608 public boolean removeService(BluetoothGattService service) {
609 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
610 if (mService == null || mServerIf == 0) return false;
611
612 BluetoothGattService intService = getService(service.getUuid(),
613 service.getInstanceId(), service.getType());
614 if (intService == null) return false;
615
616 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700617 mService.removeService(mServerIf, service.getInstanceId());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800618 mServices.remove(intService);
619 } catch (RemoteException e) {
620 Log.e(TAG,"",e);
621 return false;
622 }
623
624 return true;
625 }
626
627 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800628 * Remove all services from the list of provided services.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800629 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
630 */
631 public void clearServices() {
632 if (DBG) Log.d(TAG, "clearServices()");
633 if (mService == null || mServerIf == 0) return;
634
635 try {
636 mService.clearServices(mServerIf);
637 mServices.clear();
638 } catch (RemoteException e) {
639 Log.e(TAG,"",e);
640 }
641 }
642
643 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800644 * Returns a list of GATT services offered by this device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800645 *
646 * <p>An application must call {@link #addService} to add a serice to the
647 * list of services offered by this device.
648 *
649 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
650 *
651 * @return List of services. Returns an empty list
652 * if no services have been added yet.
653 */
654 public List<BluetoothGattService> getServices() {
655 return mServices;
656 }
657
658 /**
659 * Returns a {@link BluetoothGattService} from the list of services offered
660 * by this device.
661 *
662 * <p>If multiple instances of the same service (as identified by UUID)
663 * exist, the first instance of the service is returned.
664 *
665 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
666 *
667 * @param uuid UUID of the requested service
668 * @return BluetoothGattService if supported, or null if the requested
669 * service is not offered by this device.
670 */
671 public BluetoothGattService getService(UUID uuid) {
672 for (BluetoothGattService service : mServices) {
673 if (service.getUuid().equals(uuid)) {
674 return service;
675 }
676 }
677
678 return null;
679 }
680
Matthew Xieddf7e472013-03-01 18:41:02 -0800681
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800682 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800683 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
684 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800685 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800686 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800687 */
688 @Override
689 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800690 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800691 }
692
693 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800694 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
695 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800696 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800697 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800698 */
699 @Override
700 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -0800701 throw new UnsupportedOperationException
702 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800703 }
704
705 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800706 * Not supported - please use
707 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
708 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800709 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800710 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800711 */
712 @Override
713 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800714 throw new UnsupportedOperationException
715 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800716 }
717}