blob: 9f8d1a77a8096c43e52cb752879c39322cee2765 [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;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080054 private List<BluetoothGattService> mServices;
55
Matthew Xieddf7e472013-03-01 18:41:02 -080056 private static final int CALLBACK_REG_TIMEOUT = 10000;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080057
58 /**
59 * Bluetooth GATT interface callbacks
60 */
61 private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
62 new IBluetoothGattServerCallback.Stub() {
63 /**
64 * Application interface registered - app is ready to go
65 * @hide
66 */
67 public void onServerRegistered(int status, int serverIf) {
68 if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
69 + " serverIf=" + serverIf);
Matthew Xieddf7e472013-03-01 18:41:02 -080070 synchronized(mServerIfLock) {
71 if (mCallback != null) {
72 mServerIf = serverIf;
73 mServerIfLock.notify();
74 } else {
75 // registration timeout
76 Log.e(TAG, "onServerRegistered: mCallback is null");
77 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080078 }
79 }
80
81 /**
82 * Callback reporting an LE scan result.
83 * @hide
84 */
85 public void onScanResult(String address, int rssi, byte[] advData) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -070086 if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
Matthew Xieddf7e472013-03-01 18:41:02 -080087 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080088 }
89
90 /**
91 * Server connection state changed
92 * @hide
93 */
94 public void onServerConnectionState(int status, int serverIf,
95 boolean connected, String address) {
96 if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status
97 + " serverIf=" + serverIf + " device=" + address);
98 try {
99 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
100 connected ? BluetoothProfile.STATE_CONNECTED :
101 BluetoothProfile.STATE_DISCONNECTED);
102 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700103 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800104 }
105 }
106
107 /**
108 * Service has been added
109 * @hide
110 */
111 public void onServiceAdded(int status, int srvcType,
112 int srvcInstId, ParcelUuid srvcId) {
113 UUID srvcUuid = srvcId.getUuid();
114 if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid
115 + "status=" + status);
116
117 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
118 if (service == null) return;
119
120 try {
121 mCallback.onServiceAdded((int)status, service);
122 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700123 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800124 }
125 }
126
127 /**
128 * Remote client characteristic read request.
129 * @hide
130 */
131 public void onCharacteristicReadRequest(String address, int transId,
132 int offset, boolean isLong, int srvcType, int srvcInstId,
133 ParcelUuid srvcId, int charInstId, ParcelUuid charId) {
134 UUID srvcUuid = srvcId.getUuid();
135 UUID charUuid = charId.getUuid();
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700136 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - "
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800137 + "service=" + srvcUuid + ", characteristic=" + charUuid);
138
139 BluetoothDevice device = mAdapter.getRemoteDevice(address);
140 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
141 if (service == null) return;
142
Matthew Xieddf7e472013-03-01 18:41:02 -0800143 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800144 if (characteristic == null) return;
145
146 try {
147 mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
148 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700149 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800150 }
151 }
152
153 /**
154 * Remote client descriptor read request.
155 * @hide
156 */
157 public void onDescriptorReadRequest(String address, int transId,
158 int offset, boolean isLong, int srvcType, int srvcInstId,
159 ParcelUuid srvcId, int charInstId, ParcelUuid charId,
160 ParcelUuid descrId) {
161 UUID srvcUuid = srvcId.getUuid();
162 UUID charUuid = charId.getUuid();
163 UUID descrUuid = descrId.getUuid();
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700164 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - "
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800165 + "service=" + srvcUuid + ", characteristic=" + charUuid
166 + "descriptor=" + descrUuid);
167
168 BluetoothDevice device = mAdapter.getRemoteDevice(address);
169 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
170 if (service == null) return;
171
172 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
173 if (characteristic == null) return;
174
175 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
176 if (descriptor == null) return;
177
178 try {
179 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
180 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700181 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800182 }
183 }
184
185 /**
186 * Remote client characteristic write request.
187 * @hide
188 */
189 public void onCharacteristicWriteRequest(String address, int transId,
190 int offset, int length, boolean isPrep, boolean needRsp,
191 int srvcType, int srvcInstId, ParcelUuid srvcId,
192 int charInstId, ParcelUuid charId, byte[] value) {
193 UUID srvcUuid = srvcId.getUuid();
194 UUID charUuid = charId.getUuid();
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700195 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - "
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800196 + "service=" + srvcUuid + ", characteristic=" + charUuid);
197
198 BluetoothDevice device = mAdapter.getRemoteDevice(address);
199 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
200 if (service == null) return;
201
202 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
203 if (characteristic == null) return;
204
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 */
218 public void onDescriptorWriteRequest(String address, int transId,
219 int offset, int length, boolean isPrep, boolean needRsp,
220 int srvcType, int srvcInstId, ParcelUuid srvcId,
221 int charInstId, ParcelUuid charId, ParcelUuid descrId,
222 byte[] value) {
223 UUID srvcUuid = srvcId.getUuid();
224 UUID charUuid = charId.getUuid();
225 UUID descrUuid = descrId.getUuid();
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700226 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - "
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800227 + "service=" + srvcUuid + ", characteristic=" + charUuid
228 + "descriptor=" + descrUuid);
229
230 BluetoothDevice device = mAdapter.getRemoteDevice(address);
231
232 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
233 if (service == null) return;
234
235 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
236 if (characteristic == null) return;
237
238 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
239 if (descriptor == null) return;
240
241 try {
242 mCallback.onDescriptorWriteRequest(device, transId, descriptor,
243 isPrep, needRsp, offset, value);
244 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700245 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800246 }
247 }
248
249 /**
250 * Execute pending writes.
251 * @hide
252 */
253 public void onExecuteWrite(String address, int transId,
254 boolean execWrite) {
255 if (DBG) Log.d(TAG, "onExecuteWrite() - "
256 + "device=" + address + ", transId=" + transId
257 + "execWrite=" + execWrite);
258
259 BluetoothDevice device = mAdapter.getRemoteDevice(address);
260 if (device == null) return;
261
262 try {
263 mCallback.onExecuteWrite(device, transId, execWrite);
264 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700265 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800266 }
267 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700268
269 /**
270 * A notification/indication has been sent.
271 * @hide
272 */
273 public void onNotificationSent(String address, int status) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700274 if (VDBG) Log.d(TAG, "onNotificationSent() - "
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700275 + "device=" + address + ", status=" + status);
276
277 BluetoothDevice device = mAdapter.getRemoteDevice(address);
278 if (device == null) return;
279
280 try {
281 mCallback.onNotificationSent(device, status);
282 } catch (Exception ex) {
283 Log.w(TAG, "Unhandled exception: " + ex);
284 }
285 }
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800286
287 /**
288 * The MTU for a connection has changed
289 * @hide
290 */
291 public void onMtuChanged(String address, int mtu) {
292 if (DBG) Log.d(TAG, "onMtuChanged() - "
293 + "device=" + address + ", mtu=" + mtu);
294
295 BluetoothDevice device = mAdapter.getRemoteDevice(address);
296 if (device == null) return;
297
298 try {
299 mCallback.onMtuChanged(device, mtu);
300 } catch (Exception ex) {
301 Log.w(TAG, "Unhandled exception: " + ex);
302 }
303 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800304 };
305
306 /**
307 * Create a BluetoothGattServer proxy object.
308 */
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700309 /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800310 mService = iGatt;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800311 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xieddf7e472013-03-01 18:41:02 -0800312 mCallback = null;
313 mServerIf = 0;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700314 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800315 mServices = new ArrayList<BluetoothGattService>();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800316 }
317
318 /**
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700319 * Close this GATT server instance.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700320 *
321 * Application should call this method as early as possible after it is done with
322 * this GATT server.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800323 */
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700324 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800325 if (DBG) Log.d(TAG, "close()");
Matthew Xieddf7e472013-03-01 18:41:02 -0800326 unregisterCallback();
327 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800328
Matthew Xieddf7e472013-03-01 18:41:02 -0800329 /**
330 * Register an application callback to start using GattServer.
331 *
332 * <p>This is an asynchronous call. The callback is used to notify
333 * success or failure if the function returns true.
334 *
335 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
336 *
337 * @param callback GATT callback handler that will receive asynchronous
338 * callbacks.
339 * @return true, the callback will be called to notify success or failure,
340 * false on immediate error
341 */
342 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
343 if (DBG) Log.d(TAG, "registerCallback()");
344 if (mService == null) {
345 Log.e(TAG, "GATT service not available");
346 return false;
347 }
348 UUID uuid = UUID.randomUUID();
349 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800350
Matthew Xieddf7e472013-03-01 18:41:02 -0800351 synchronized(mServerIfLock) {
352 if (mCallback != null) {
353 Log.e(TAG, "App can register callback only once");
354 return false;
355 }
356
357 mCallback = callback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800358 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800359 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
360 } catch (RemoteException e) {
361 Log.e(TAG,"",e);
362 mCallback = null;
363 return false;
364 }
365
366 try {
367 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
368 } catch (InterruptedException e) {
369 Log.e(TAG, "" + e);
370 mCallback = null;
371 }
372
373 if (mServerIf == 0) {
374 mCallback = null;
375 return false;
376 } else {
377 return true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800378 }
379 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800380 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800381
Matthew Xieddf7e472013-03-01 18:41:02 -0800382 /**
383 * Unregister the current application and callbacks.
384 */
385 private void unregisterCallback() {
386 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
387 if (mService == null || mServerIf == 0) return;
388
389 try {
390 mCallback = null;
391 mService.unregisterServer(mServerIf);
392 mServerIf = 0;
393 } catch (RemoteException e) {
394 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800395 }
396 }
397
398 /**
399 * Returns a service by UUID, instance and type.
400 * @hide
401 */
402 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
403 for(BluetoothGattService svc : mServices) {
404 if (svc.getType() == type &&
405 svc.getInstanceId() == instanceId &&
406 svc.getUuid().equals(uuid)) {
407 return svc;
408 }
409 }
410 return null;
411 }
412
413 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800414 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800415 *
416 * <p>The connection may not be established right away, but will be
417 * completed when the remote device is available. A
Andre Eisenbach3f366602013-03-08 18:42:24 -0800418 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800419 * invoked when the connection state changes as a result of this function.
420 *
421 * <p>The autoConnect paramter determines whether to actively connect to
422 * the remote device, or rather passively scan and finalize the connection
423 * when the remote device is in range/available. Generally, the first ever
424 * connection to a device should be direct (autoConnect set to false) and
425 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800426 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800427 *
428 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
429 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800430 * @param autoConnect Whether to directly connect to the remote device (false)
431 * or to automatically connect as soon as the remote
432 * device becomes available (true).
433 * @return true, if the connection attempt was initiated successfully
434 */
435 public boolean connect(BluetoothDevice device, boolean autoConnect) {
Andre Eisenbach3f366602013-03-08 18:42:24 -0800436 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800437 if (mService == null || mServerIf == 0) return false;
438
439 try {
440 mService.serverConnect(mServerIf, device.getAddress(),
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700441 autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect"
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800442 } catch (RemoteException e) {
443 Log.e(TAG,"",e);
444 return false;
445 }
446
447 return true;
448 }
449
450 /**
451 * Disconnects an established connection, or cancels a connection attempt
452 * currently in progress.
453 *
454 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
455 *
456 * @param device Remote device
457 */
458 public void cancelConnection(BluetoothDevice device) {
459 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
460 if (mService == null || mServerIf == 0) return;
461
462 try {
463 mService.serverDisconnect(mServerIf, device.getAddress());
464 } catch (RemoteException e) {
465 Log.e(TAG,"",e);
466 }
467 }
468
469 /**
470 * Send a response to a read or write request to a remote device.
471 *
472 * <p>This function must be invoked in when a remote read/write request
Matthew Xieddf7e472013-03-01 18:41:02 -0800473 * is received by one of these callback methods:
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800474 *
475 * <ul>
476 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
477 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
478 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
479 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
480 * </ul>
481 *
482 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
483 *
484 * @param device The remote device to send this response to
485 * @param requestId The ID of the request that was received with the callback
486 * @param status The status of the request to be sent to the remote devices
487 * @param offset Value offset for partial read/write response
488 * @param value The value of the attribute that was read/written (optional)
489 */
490 public boolean sendResponse(BluetoothDevice device, int requestId,
491 int status, int offset, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700492 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800493 if (mService == null || mServerIf == 0) return false;
494
495 try {
496 mService.sendResponse(mServerIf, device.getAddress(), requestId,
497 status, offset, value);
498 } catch (RemoteException e) {
499 Log.e(TAG,"",e);
500 return false;
501 }
502 return true;
503 }
504
505 /**
506 * Send a notification or indication that a local characteristic has been
507 * updated.
508 *
509 * <p>A notification or indication is sent to the remote device to signal
510 * that the characteristic has been updated. This function should be invoked
511 * for every client that requests notifications/indications by writing
512 * to the "Client Configuration" descriptor for the given characteristic.
513 *
514 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
515 *
516 * @param device The remote device to receive the notification/indication
517 * @param characteristic The local characteristic that has been updated
518 * @param confirm true to request confirmation from the client (indication),
519 * false to send a notification
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700520 * @throws IllegalArgumentException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800521 * @return true, if the notification has been triggered successfully
522 */
523 public boolean notifyCharacteristicChanged(BluetoothDevice device,
524 BluetoothGattCharacteristic characteristic, boolean confirm) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700525 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800526 if (mService == null || mServerIf == 0) return false;
527
528 BluetoothGattService service = characteristic.getService();
529 if (service == null) return false;
530
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700531 if (characteristic.getValue() == null) {
532 throw new IllegalArgumentException("Chracteristic value is empty. Use "
533 + "BluetoothGattCharacteristic#setvalue to update");
534 }
535
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800536 try {
537 mService.sendNotification(mServerIf, device.getAddress(),
538 service.getType(), service.getInstanceId(),
539 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
540 new ParcelUuid(characteristic.getUuid()), confirm,
541 characteristic.getValue());
542 } catch (RemoteException e) {
543 Log.e(TAG,"",e);
544 return false;
545 }
546
547 return true;
548 }
549
550 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800551 * Add a service to the list of services to be hosted.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800552 *
Andre Eisenbach061cfd02014-06-27 14:31:37 -0700553 * <p>Once a service has been addded to the the list, the service and its
Matthew Xieddf7e472013-03-01 18:41:02 -0800554 * included characteristics will be provided by the local device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800555 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800556 * <p>If the local device has already exposed services when this function
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800557 * is called, a service update notification will be sent to all clients.
558 *
559 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
560 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800561 * @param service Service to be added to the list of services provided
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800562 * by this device.
563 * @return true, if the service has been added successfully
564 */
565 public boolean addService(BluetoothGattService service) {
566 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
567 if (mService == null || mServerIf == 0) return false;
568
569 mServices.add(service);
570
571 try {
572 mService.beginServiceDeclaration(mServerIf, service.getType(),
573 service.getInstanceId(), service.getHandles(),
Wei Wang18c76932013-10-29 21:05:37 -0700574 new ParcelUuid(service.getUuid()), service.isAdvertisePreferred());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800575
576 List<BluetoothGattService> includedServices = service.getIncludedServices();
577 for (BluetoothGattService includedService : includedServices) {
578 mService.addIncludedService(mServerIf,
579 includedService.getType(),
580 includedService.getInstanceId(),
581 new ParcelUuid(includedService.getUuid()));
582 }
583
584 List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
585 for (BluetoothGattCharacteristic characteristic : characteristics) {
586 int permission = ((characteristic.getKeySize() - 7) << 12)
587 + characteristic.getPermissions();
588 mService.addCharacteristic(mServerIf,
589 new ParcelUuid(characteristic.getUuid()),
590 characteristic.getProperties(), permission);
591
592 List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
593 for (BluetoothGattDescriptor descriptor: descriptors) {
Andre Eisenbach6f0ed122013-04-03 14:26:43 -0700594 permission = ((characteristic.getKeySize() - 7) << 12)
595 + descriptor.getPermissions();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800596 mService.addDescriptor(mServerIf,
Andre Eisenbach6f0ed122013-04-03 14:26:43 -0700597 new ParcelUuid(descriptor.getUuid()), permission);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800598 }
599 }
600
601 mService.endServiceDeclaration(mServerIf);
602 } catch (RemoteException e) {
603 Log.e(TAG,"",e);
604 return false;
605 }
606
607 return true;
608 }
609
610 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800611 * Removes a service from the list of services to be provided.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800612 *
613 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
614 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800615 * @param service Service to be removed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800616 * @return true, if the service has been removed
617 */
618 public boolean removeService(BluetoothGattService service) {
619 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
620 if (mService == null || mServerIf == 0) return false;
621
622 BluetoothGattService intService = getService(service.getUuid(),
623 service.getInstanceId(), service.getType());
624 if (intService == null) return false;
625
626 try {
627 mService.removeService(mServerIf, service.getType(),
628 service.getInstanceId(), new ParcelUuid(service.getUuid()));
629 mServices.remove(intService);
630 } catch (RemoteException e) {
631 Log.e(TAG,"",e);
632 return false;
633 }
634
635 return true;
636 }
637
638 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800639 * Remove all services from the list of provided services.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800640 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
641 */
642 public void clearServices() {
643 if (DBG) Log.d(TAG, "clearServices()");
644 if (mService == null || mServerIf == 0) return;
645
646 try {
647 mService.clearServices(mServerIf);
648 mServices.clear();
649 } catch (RemoteException e) {
650 Log.e(TAG,"",e);
651 }
652 }
653
654 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800655 * Returns a list of GATT services offered by this device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800656 *
657 * <p>An application must call {@link #addService} to add a serice to the
658 * list of services offered by this device.
659 *
660 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
661 *
662 * @return List of services. Returns an empty list
663 * if no services have been added yet.
664 */
665 public List<BluetoothGattService> getServices() {
666 return mServices;
667 }
668
669 /**
670 * Returns a {@link BluetoothGattService} from the list of services offered
671 * by this device.
672 *
673 * <p>If multiple instances of the same service (as identified by UUID)
674 * exist, the first instance of the service is returned.
675 *
676 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
677 *
678 * @param uuid UUID of the requested service
679 * @return BluetoothGattService if supported, or null if the requested
680 * service is not offered by this device.
681 */
682 public BluetoothGattService getService(UUID uuid) {
683 for (BluetoothGattService service : mServices) {
684 if (service.getUuid().equals(uuid)) {
685 return service;
686 }
687 }
688
689 return null;
690 }
691
Matthew Xieddf7e472013-03-01 18:41:02 -0800692
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800693 /**
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 int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800701 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 }
703
704 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800705 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
706 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800707 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800708 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800709 */
710 @Override
711 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -0800712 throw new UnsupportedOperationException
713 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800714 }
715
716 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800717 * Not supported - please use
718 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
719 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800720 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800721 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800722 */
723 @Override
724 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800725 throw new UnsupportedOperationException
726 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800727 }
728}