blob: 58ee54fdb1004525a7e8ac63b273707849c97c7b [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;
22import android.bluetooth.BluetoothProfile.ServiceListener;
23import android.bluetooth.IBluetoothManager;
24import android.bluetooth.IBluetoothStateChangeCallback;
25
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.ServiceConnection;
30import android.os.IBinder;
31import android.os.ParcelUuid;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.util.Log;
35
36import java.util.ArrayList;
37import java.util.List;
38import java.util.UUID;
39
40/**
Matthew Xieddf7e472013-03-01 18:41:02 -080041 * Public API for the Bluetooth GATT Profile server role.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080042 *
Matthew Xieddf7e472013-03-01 18:41:02 -080043 * <p>This class provides Bluetooth GATT server role functionality,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080044 * allowing applications to create and advertise Bluetooth Smart services
45 * and characteristics.
46 *
47 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
48 * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
49 * BluetoothGatt proxy object.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080050 */
51public final class BluetoothGattServer implements BluetoothProfile {
52 private static final String TAG = "BluetoothGattServer";
53 private static final boolean DBG = true;
54
Matthew Xieddf7e472013-03-01 18:41:02 -080055 private final Context mContext;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080056 private BluetoothAdapter mAdapter;
57 private IBluetoothGatt mService;
58 private BluetoothGattServerCallback mCallback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080059
Matthew Xieddf7e472013-03-01 18:41:02 -080060 private Object mServerIfLock = new Object();
61 private int mServerIf;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080062 private List<BluetoothGattService> mServices;
63
Matthew Xieddf7e472013-03-01 18:41:02 -080064 private static final int CALLBACK_REG_TIMEOUT = 10000;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080065
66 /**
67 * Bluetooth GATT interface callbacks
68 */
69 private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
70 new IBluetoothGattServerCallback.Stub() {
71 /**
72 * Application interface registered - app is ready to go
73 * @hide
74 */
75 public void onServerRegistered(int status, int serverIf) {
76 if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
77 + " serverIf=" + serverIf);
Matthew Xieddf7e472013-03-01 18:41:02 -080078 synchronized(mServerIfLock) {
79 if (mCallback != null) {
80 mServerIf = serverIf;
81 mServerIfLock.notify();
82 } else {
83 // registration timeout
84 Log.e(TAG, "onServerRegistered: mCallback is null");
85 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080086 }
87 }
88
89 /**
90 * Callback reporting an LE scan result.
91 * @hide
92 */
93 public void onScanResult(String address, int rssi, byte[] advData) {
94 if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
Matthew Xieddf7e472013-03-01 18:41:02 -080095 // no op
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080096 }
97
98 /**
99 * Server connection state changed
100 * @hide
101 */
102 public void onServerConnectionState(int status, int serverIf,
103 boolean connected, String address) {
104 if (DBG) Log.d(TAG, "onServerConnectionState() - status=" + status
105 + " serverIf=" + serverIf + " device=" + address);
106 try {
107 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
108 connected ? BluetoothProfile.STATE_CONNECTED :
109 BluetoothProfile.STATE_DISCONNECTED);
110 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700111 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800112 }
113 }
114
115 /**
116 * Service has been added
117 * @hide
118 */
119 public void onServiceAdded(int status, int srvcType,
120 int srvcInstId, ParcelUuid srvcId) {
121 UUID srvcUuid = srvcId.getUuid();
122 if (DBG) Log.d(TAG, "onServiceAdded() - service=" + srvcUuid
123 + "status=" + status);
124
125 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
126 if (service == null) return;
127
128 try {
129 mCallback.onServiceAdded((int)status, service);
130 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700131 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800132 }
133 }
134
135 /**
136 * Remote client characteristic read request.
137 * @hide
138 */
139 public void onCharacteristicReadRequest(String address, int transId,
140 int offset, boolean isLong, int srvcType, int srvcInstId,
141 ParcelUuid srvcId, int charInstId, ParcelUuid charId) {
142 UUID srvcUuid = srvcId.getUuid();
143 UUID charUuid = charId.getUuid();
144 if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - "
145 + "service=" + srvcUuid + ", characteristic=" + charUuid);
146
147 BluetoothDevice device = mAdapter.getRemoteDevice(address);
148 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
149 if (service == null) return;
150
Matthew Xieddf7e472013-03-01 18:41:02 -0800151 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800152 if (characteristic == null) return;
153
154 try {
155 mCallback.onCharacteristicReadRequest(device, transId, offset, characteristic);
156 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700157 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800158 }
159 }
160
161 /**
162 * Remote client descriptor read request.
163 * @hide
164 */
165 public void onDescriptorReadRequest(String address, int transId,
166 int offset, boolean isLong, int srvcType, int srvcInstId,
167 ParcelUuid srvcId, int charInstId, ParcelUuid charId,
168 ParcelUuid descrId) {
169 UUID srvcUuid = srvcId.getUuid();
170 UUID charUuid = charId.getUuid();
171 UUID descrUuid = descrId.getUuid();
172 if (DBG) Log.d(TAG, "onCharacteristicReadRequest() - "
173 + "service=" + srvcUuid + ", characteristic=" + charUuid
174 + "descriptor=" + descrUuid);
175
176 BluetoothDevice device = mAdapter.getRemoteDevice(address);
177 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
178 if (service == null) return;
179
180 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
181 if (characteristic == null) return;
182
183 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
184 if (descriptor == null) return;
185
186 try {
187 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
188 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700189 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800190 }
191 }
192
193 /**
194 * Remote client characteristic write request.
195 * @hide
196 */
197 public void onCharacteristicWriteRequest(String address, int transId,
198 int offset, int length, boolean isPrep, boolean needRsp,
199 int srvcType, int srvcInstId, ParcelUuid srvcId,
200 int charInstId, ParcelUuid charId, byte[] value) {
201 UUID srvcUuid = srvcId.getUuid();
202 UUID charUuid = charId.getUuid();
203 if (DBG) Log.d(TAG, "onCharacteristicWriteRequest() - "
204 + "service=" + srvcUuid + ", characteristic=" + charUuid);
205
206 BluetoothDevice device = mAdapter.getRemoteDevice(address);
207 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
208 if (service == null) return;
209
210 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
211 if (characteristic == null) return;
212
213 try {
214 mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
215 isPrep, needRsp, offset, value);
216 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700217 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800218 }
219
220 }
221
222 /**
223 * Remote client descriptor write request.
224 * @hide
225 */
226 public void onDescriptorWriteRequest(String address, int transId,
227 int offset, int length, boolean isPrep, boolean needRsp,
228 int srvcType, int srvcInstId, ParcelUuid srvcId,
229 int charInstId, ParcelUuid charId, ParcelUuid descrId,
230 byte[] value) {
231 UUID srvcUuid = srvcId.getUuid();
232 UUID charUuid = charId.getUuid();
233 UUID descrUuid = descrId.getUuid();
234 if (DBG) Log.d(TAG, "onDescriptorWriteRequest() - "
235 + "service=" + srvcUuid + ", characteristic=" + charUuid
236 + "descriptor=" + descrUuid);
237
238 BluetoothDevice device = mAdapter.getRemoteDevice(address);
239
240 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
241 if (service == null) return;
242
243 BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
244 if (characteristic == null) return;
245
246 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descrUuid);
247 if (descriptor == null) return;
248
249 try {
250 mCallback.onDescriptorWriteRequest(device, transId, descriptor,
251 isPrep, needRsp, offset, value);
252 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700253 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800254 }
255 }
256
257 /**
258 * Execute pending writes.
259 * @hide
260 */
261 public void onExecuteWrite(String address, int transId,
262 boolean execWrite) {
263 if (DBG) Log.d(TAG, "onExecuteWrite() - "
264 + "device=" + address + ", transId=" + transId
265 + "execWrite=" + execWrite);
266
267 BluetoothDevice device = mAdapter.getRemoteDevice(address);
268 if (device == null) return;
269
270 try {
271 mCallback.onExecuteWrite(device, transId, execWrite);
272 } catch (Exception ex) {
Mike Lockwood0998ff12013-05-13 14:04:12 -0700273 Log.w(TAG, "Unhandled exception in callback", ex);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800274 }
275 }
276 };
277
278 /**
279 * Create a BluetoothGattServer proxy object.
280 */
Matthew Xieddf7e472013-03-01 18:41:02 -0800281 /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800282 mContext = context;
Matthew Xieddf7e472013-03-01 18:41:02 -0800283 mService = iGatt;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800284 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xieddf7e472013-03-01 18:41:02 -0800285 mCallback = null;
286 mServerIf = 0;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800287 mServices = new ArrayList<BluetoothGattService>();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800288 }
289
290 /**
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700291 * Close this GATT server instance.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700292 *
293 * Application should call this method as early as possible after it is done with
294 * this GATT server.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800295 */
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700296 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800297 if (DBG) Log.d(TAG, "close()");
Matthew Xieddf7e472013-03-01 18:41:02 -0800298 unregisterCallback();
299 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800300
Matthew Xieddf7e472013-03-01 18:41:02 -0800301 /**
302 * Register an application callback to start using GattServer.
303 *
304 * <p>This is an asynchronous call. The callback is used to notify
305 * success or failure if the function returns true.
306 *
307 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
308 *
309 * @param callback GATT callback handler that will receive asynchronous
310 * callbacks.
311 * @return true, the callback will be called to notify success or failure,
312 * false on immediate error
313 */
314 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
315 if (DBG) Log.d(TAG, "registerCallback()");
316 if (mService == null) {
317 Log.e(TAG, "GATT service not available");
318 return false;
319 }
320 UUID uuid = UUID.randomUUID();
321 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800322
Matthew Xieddf7e472013-03-01 18:41:02 -0800323 synchronized(mServerIfLock) {
324 if (mCallback != null) {
325 Log.e(TAG, "App can register callback only once");
326 return false;
327 }
328
329 mCallback = callback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800330 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800331 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
332 } catch (RemoteException e) {
333 Log.e(TAG,"",e);
334 mCallback = null;
335 return false;
336 }
337
338 try {
339 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
340 } catch (InterruptedException e) {
341 Log.e(TAG, "" + e);
342 mCallback = null;
343 }
344
345 if (mServerIf == 0) {
346 mCallback = null;
347 return false;
348 } else {
349 return true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800350 }
351 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800352 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800353
Matthew Xieddf7e472013-03-01 18:41:02 -0800354 /**
355 * Unregister the current application and callbacks.
356 */
357 private void unregisterCallback() {
358 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
359 if (mService == null || mServerIf == 0) return;
360
361 try {
362 mCallback = null;
363 mService.unregisterServer(mServerIf);
364 mServerIf = 0;
365 } catch (RemoteException e) {
366 Log.e(TAG,"",e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800367 }
368 }
369
370 /**
371 * Returns a service by UUID, instance and type.
372 * @hide
373 */
374 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
375 for(BluetoothGattService svc : mServices) {
376 if (svc.getType() == type &&
377 svc.getInstanceId() == instanceId &&
378 svc.getUuid().equals(uuid)) {
379 return svc;
380 }
381 }
382 return null;
383 }
384
385 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800386 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800387 *
388 * <p>The connection may not be established right away, but will be
389 * completed when the remote device is available. A
Andre Eisenbach3f366602013-03-08 18:42:24 -0800390 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800391 * invoked when the connection state changes as a result of this function.
392 *
393 * <p>The autoConnect paramter determines whether to actively connect to
394 * the remote device, or rather passively scan and finalize the connection
395 * when the remote device is in range/available. Generally, the first ever
396 * connection to a device should be direct (autoConnect set to false) and
397 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800398 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800399 *
400 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
401 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800402 * @param autoConnect Whether to directly connect to the remote device (false)
403 * or to automatically connect as soon as the remote
404 * device becomes available (true).
405 * @return true, if the connection attempt was initiated successfully
406 */
407 public boolean connect(BluetoothDevice device, boolean autoConnect) {
Andre Eisenbach3f366602013-03-08 18:42:24 -0800408 if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800409 if (mService == null || mServerIf == 0) return false;
410
411 try {
412 mService.serverConnect(mServerIf, device.getAddress(),
413 autoConnect ? false : true); // autoConnect is inverse of "isDirect"
414 } catch (RemoteException e) {
415 Log.e(TAG,"",e);
416 return false;
417 }
418
419 return true;
420 }
421
422 /**
423 * Disconnects an established connection, or cancels a connection attempt
424 * currently in progress.
425 *
426 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
427 *
428 * @param device Remote device
429 */
430 public void cancelConnection(BluetoothDevice device) {
431 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
432 if (mService == null || mServerIf == 0) return;
433
434 try {
435 mService.serverDisconnect(mServerIf, device.getAddress());
436 } catch (RemoteException e) {
437 Log.e(TAG,"",e);
438 }
439 }
440
441 /**
442 * Send a response to a read or write request to a remote device.
443 *
444 * <p>This function must be invoked in when a remote read/write request
Matthew Xieddf7e472013-03-01 18:41:02 -0800445 * is received by one of these callback methods:
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800446 *
447 * <ul>
448 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
449 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
450 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
451 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
452 * </ul>
453 *
454 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
455 *
456 * @param device The remote device to send this response to
457 * @param requestId The ID of the request that was received with the callback
458 * @param status The status of the request to be sent to the remote devices
459 * @param offset Value offset for partial read/write response
460 * @param value The value of the attribute that was read/written (optional)
461 */
462 public boolean sendResponse(BluetoothDevice device, int requestId,
463 int status, int offset, byte[] value) {
464 if (DBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
465 if (mService == null || mServerIf == 0) return false;
466
467 try {
468 mService.sendResponse(mServerIf, device.getAddress(), requestId,
469 status, offset, value);
470 } catch (RemoteException e) {
471 Log.e(TAG,"",e);
472 return false;
473 }
474 return true;
475 }
476
477 /**
478 * Send a notification or indication that a local characteristic has been
479 * updated.
480 *
481 * <p>A notification or indication is sent to the remote device to signal
482 * that the characteristic has been updated. This function should be invoked
483 * for every client that requests notifications/indications by writing
484 * to the "Client Configuration" descriptor for the given characteristic.
485 *
486 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
487 *
488 * @param device The remote device to receive the notification/indication
489 * @param characteristic The local characteristic that has been updated
490 * @param confirm true to request confirmation from the client (indication),
491 * false to send a notification
492 * @return true, if the notification has been triggered successfully
493 */
494 public boolean notifyCharacteristicChanged(BluetoothDevice device,
495 BluetoothGattCharacteristic characteristic, boolean confirm) {
496 if (DBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
497 if (mService == null || mServerIf == 0) return false;
498
499 BluetoothGattService service = characteristic.getService();
500 if (service == null) return false;
501
502 try {
503 mService.sendNotification(mServerIf, device.getAddress(),
504 service.getType(), service.getInstanceId(),
505 new ParcelUuid(service.getUuid()), characteristic.getInstanceId(),
506 new ParcelUuid(characteristic.getUuid()), confirm,
507 characteristic.getValue());
508 } catch (RemoteException e) {
509 Log.e(TAG,"",e);
510 return false;
511 }
512
513 return true;
514 }
515
516 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800517 * Add a service to the list of services to be hosted.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800518 *
519 * <p>Once a service has been addded to the the list, the service and it's
Matthew Xieddf7e472013-03-01 18:41:02 -0800520 * included characteristics will be provided by the local device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800521 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800522 * <p>If the local device has already exposed services when this function
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 * is called, a service update notification will be sent to all clients.
524 *
525 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
526 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800527 * @param service Service to be added to the list of services provided
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800528 * by this device.
529 * @return true, if the service has been added successfully
530 */
531 public boolean addService(BluetoothGattService service) {
532 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
533 if (mService == null || mServerIf == 0) return false;
534
535 mServices.add(service);
536
537 try {
538 mService.beginServiceDeclaration(mServerIf, service.getType(),
539 service.getInstanceId(), service.getHandles(),
540 new ParcelUuid(service.getUuid()));
541
542 List<BluetoothGattService> includedServices = service.getIncludedServices();
543 for (BluetoothGattService includedService : includedServices) {
544 mService.addIncludedService(mServerIf,
545 includedService.getType(),
546 includedService.getInstanceId(),
547 new ParcelUuid(includedService.getUuid()));
548 }
549
550 List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
551 for (BluetoothGattCharacteristic characteristic : characteristics) {
552 int permission = ((characteristic.getKeySize() - 7) << 12)
553 + characteristic.getPermissions();
554 mService.addCharacteristic(mServerIf,
555 new ParcelUuid(characteristic.getUuid()),
556 characteristic.getProperties(), permission);
557
558 List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
559 for (BluetoothGattDescriptor descriptor: descriptors) {
Andre Eisenbach6f0ed122013-04-03 14:26:43 -0700560 permission = ((characteristic.getKeySize() - 7) << 12)
561 + descriptor.getPermissions();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800562 mService.addDescriptor(mServerIf,
Andre Eisenbach6f0ed122013-04-03 14:26:43 -0700563 new ParcelUuid(descriptor.getUuid()), permission);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800564 }
565 }
566
567 mService.endServiceDeclaration(mServerIf);
568 } catch (RemoteException e) {
569 Log.e(TAG,"",e);
570 return false;
571 }
572
573 return true;
574 }
575
576 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800577 * Removes a service from the list of services to be provided.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800578 *
579 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
580 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800581 * @param service Service to be removed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800582 * @return true, if the service has been removed
583 */
584 public boolean removeService(BluetoothGattService service) {
585 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
586 if (mService == null || mServerIf == 0) return false;
587
588 BluetoothGattService intService = getService(service.getUuid(),
589 service.getInstanceId(), service.getType());
590 if (intService == null) return false;
591
592 try {
593 mService.removeService(mServerIf, service.getType(),
594 service.getInstanceId(), new ParcelUuid(service.getUuid()));
595 mServices.remove(intService);
596 } catch (RemoteException e) {
597 Log.e(TAG,"",e);
598 return false;
599 }
600
601 return true;
602 }
603
604 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800605 * Remove all services from the list of provided services.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800606 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
607 */
608 public void clearServices() {
609 if (DBG) Log.d(TAG, "clearServices()");
610 if (mService == null || mServerIf == 0) return;
611
612 try {
613 mService.clearServices(mServerIf);
614 mServices.clear();
615 } catch (RemoteException e) {
616 Log.e(TAG,"",e);
617 }
618 }
619
620 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800621 * Returns a list of GATT services offered by this device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800622 *
623 * <p>An application must call {@link #addService} to add a serice to the
624 * list of services offered by this device.
625 *
626 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
627 *
628 * @return List of services. Returns an empty list
629 * if no services have been added yet.
630 */
631 public List<BluetoothGattService> getServices() {
632 return mServices;
633 }
634
635 /**
636 * Returns a {@link BluetoothGattService} from the list of services offered
637 * by this device.
638 *
639 * <p>If multiple instances of the same service (as identified by UUID)
640 * exist, the first instance of the service is returned.
641 *
642 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
643 *
644 * @param uuid UUID of the requested service
645 * @return BluetoothGattService if supported, or null if the requested
646 * service is not offered by this device.
647 */
648 public BluetoothGattService getService(UUID uuid) {
649 for (BluetoothGattService service : mServices) {
650 if (service.getUuid().equals(uuid)) {
651 return service;
652 }
653 }
654
655 return null;
656 }
657
Matthew Xieddf7e472013-03-01 18:41:02 -0800658
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800659 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800660 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
661 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800662 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800663 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800664 */
665 @Override
666 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800667 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800668 }
669
670 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800671 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
672 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800673 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800674 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800675 */
676 @Override
677 public List<BluetoothDevice> getConnectedDevices() {
Matthew Xieddf7e472013-03-01 18:41:02 -0800678 throw new UnsupportedOperationException
679 ("Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800680 }
681
682 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800683 * Not supported - please use
684 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
685 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800686 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800687 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800688 */
689 @Override
690 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800691 throw new UnsupportedOperationException
692 ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800693 }
694}