blob: ef1b0bd718854cdba7cae3a5887348d67494680f [file] [log] [blame]
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080019import android.os.ParcelUuid;
20import android.os.RemoteException;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080021import android.util.Log;
22
23import java.util.ArrayList;
24import java.util.List;
25import java.util.UUID;
26
27/**
Matthew Xieddf7e472013-03-01 18:41:02 -080028 * Public API for the Bluetooth GATT Profile server role.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080029 *
Matthew Xieddf7e472013-03-01 18:41:02 -080030 * <p>This class provides Bluetooth GATT server role functionality,
Andre Eisenbach061cfd02014-06-27 14:31:37 -070031 * allowing applications to create Bluetooth Smart services and
32 * characteristics.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080033 *
34 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
Andre Eisenbach061cfd02014-06-27 14:31:37 -070035 * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance
36 * of this class.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080037 */
38public final class BluetoothGattServer implements BluetoothProfile {
39 private static final String TAG = "BluetoothGattServer";
40 private static final boolean DBG = true;
Andre Eisenbach55d19e42014-07-18 14:38:36 -070041 private static final boolean VDBG = false;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080042
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080043 private BluetoothAdapter mAdapter;
44 private IBluetoothGatt mService;
Jakub Pawlowskid64bb882017-03-22 11:22:18 -070045 private BluetoothGattServerCallback mCallback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080046
Matthew Xieddf7e472013-03-01 18:41:02 -080047 private Object mServerIfLock = new Object();
48 private int mServerIf;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -070049 private int mTransport;
Jakub Pawlowskid75f5122016-04-01 07:51:45 -070050 private BluetoothGattService mPendingService;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080051 private List<BluetoothGattService> mServices;
52
Matthew Xieddf7e472013-03-01 18:41:02 -080053 private static final int CALLBACK_REG_TIMEOUT = 10000;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -080054
55 /**
56 * Bluetooth GATT interface callbacks
57 */
Jakub Pawlowskid7116be2017-03-27 12:14:40 -070058 private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
Jack Hea355e5e2017-08-22 16:06:54 -070059 new IBluetoothGattServerCallback.Stub() {
60 /**
61 * Application interface registered - app is ready to go
62 * @hide
63 */
64 @Override
65 public void onServerRegistered(int status, int serverIf) {
66 if (DBG) {
67 Log.d(TAG, "onServerRegistered() - status=" + status
68 + " serverIf=" + serverIf);
Matthew Xieddf7e472013-03-01 18:41:02 -080069 }
Jack Hea355e5e2017-08-22 16:06:54 -070070 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 }
Jakub Pawlowskid75f5122016-04-01 07:51:45 -070078 }
79 }
80
Jack Hea355e5e2017-08-22 16:06:54 -070081 /**
82 * Server connection state changed
83 * @hide
84 */
85 @Override
86 public void onServerConnectionState(int status, int serverIf,
87 boolean connected, String address) {
88 if (DBG) {
89 Log.d(TAG, "onServerConnectionState() - status=" + status
90 + " serverIf=" + serverIf + " device=" + address);
91 }
92 try {
93 mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
94 connected ? BluetoothProfile.STATE_CONNECTED :
95 BluetoothProfile.STATE_DISCONNECTED);
96 } catch (Exception ex) {
97 Log.w(TAG, "Unhandled exception in callback", ex);
98 }
Jakub Pawlowskid75f5122016-04-01 07:51:45 -070099 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800100
Jack Hea355e5e2017-08-22 16:06:54 -0700101 /**
102 * Service has been added
103 * @hide
104 */
105 @Override
106 public void onServiceAdded(int status, BluetoothGattService service) {
107 if (DBG) {
108 Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId()
109 + " uuid=" + service.getUuid() + " status=" + status);
110 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800111
Jack Hea355e5e2017-08-22 16:06:54 -0700112 if (mPendingService == null) {
113 return;
114 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800115
Jack Hea355e5e2017-08-22 16:06:54 -0700116 BluetoothGattService tmp = mPendingService;
117 mPendingService = null;
118
119 // Rewrite newly assigned handles to existing service.
120 tmp.setInstanceId(service.getInstanceId());
121 List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics();
122 List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics();
123 for (int i = 0; i < svc_chars.size(); i++) {
124 BluetoothGattCharacteristic temp_char = temp_chars.get(i);
125 BluetoothGattCharacteristic svc_char = svc_chars.get(i);
126
127 temp_char.setInstanceId(svc_char.getInstanceId());
128
129 List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors();
130 List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors();
131 for (int j = 0; j < svc_descs.size(); j++) {
132 temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId());
133 }
134 }
135
136 mServices.add(tmp);
137
138 try {
139 mCallback.onServiceAdded((int) status, tmp);
140 } catch (Exception ex) {
141 Log.w(TAG, "Unhandled exception in callback", ex);
142 }
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700143 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800144
Jack Hea355e5e2017-08-22 16:06:54 -0700145 /**
146 * Remote client characteristic read request.
147 * @hide
148 */
149 @Override
150 public void onCharacteristicReadRequest(String address, int transId,
151 int offset, boolean isLong, int handle) {
152 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800153
Jack Hea355e5e2017-08-22 16:06:54 -0700154 BluetoothDevice device = mAdapter.getRemoteDevice(address);
155 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
156 if (characteristic == null) {
157 Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle);
158 return;
159 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800160
Jack Hea355e5e2017-08-22 16:06:54 -0700161 try {
162 mCallback.onCharacteristicReadRequest(device, transId, offset,
163 characteristic);
164 } catch (Exception ex) {
165 Log.w(TAG, "Unhandled exception in callback", ex);
166 }
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700167 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800168
Jack Hea355e5e2017-08-22 16:06:54 -0700169 /**
170 * Remote client descriptor read request.
171 * @hide
172 */
173 @Override
174 public void onDescriptorReadRequest(String address, int transId,
175 int offset, boolean isLong, int handle) {
176 if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle);
177
178 BluetoothDevice device = mAdapter.getRemoteDevice(address);
179 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
180 if (descriptor == null) {
181 Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle);
182 return;
183 }
184
185 try {
186 mCallback.onDescriptorReadRequest(device, transId, offset, descriptor);
187 } catch (Exception ex) {
188 Log.w(TAG, "Unhandled exception in callback", ex);
189 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800190 }
191
Jack Hea355e5e2017-08-22 16:06:54 -0700192 /**
193 * Remote client characteristic write request.
194 * @hide
195 */
196 @Override
197 public void onCharacteristicWriteRequest(String address, int transId,
198 int offset, int length, boolean isPrep, boolean needRsp,
199 int handle, byte[] value) {
200 if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800201
Jack Hea355e5e2017-08-22 16:06:54 -0700202 BluetoothDevice device = mAdapter.getRemoteDevice(address);
203 BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle);
204 if (characteristic == null) {
205 Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle);
206 return;
207 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800208
Jack Hea355e5e2017-08-22 16:06:54 -0700209 try {
210 mCallback.onCharacteristicWriteRequest(device, transId, characteristic,
211 isPrep, needRsp, offset, value);
212 } catch (Exception ex) {
213 Log.w(TAG, "Unhandled exception in callback", ex);
214 }
215
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700216 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800217
Jack Hea355e5e2017-08-22 16:06:54 -0700218 /**
219 * Remote client descriptor write request.
220 * @hide
221 */
222 @Override
223 public void onDescriptorWriteRequest(String address, int transId, int offset,
224 int length, boolean isPrep, boolean needRsp, int handle, byte[] value) {
225 if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle);
226
227 BluetoothDevice device = mAdapter.getRemoteDevice(address);
228 BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle);
229 if (descriptor == null) {
230 Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle);
231 return;
232 }
233
234 try {
235 mCallback.onDescriptorWriteRequest(device, transId, descriptor,
236 isPrep, needRsp, offset, value);
237 } catch (Exception ex) {
238 Log.w(TAG, "Unhandled exception in callback", ex);
239 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800240 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800241
Jack Hea355e5e2017-08-22 16:06:54 -0700242 /**
243 * Execute pending writes.
244 * @hide
245 */
246 @Override
247 public void onExecuteWrite(String address, int transId,
248 boolean execWrite) {
249 if (DBG) {
250 Log.d(TAG, "onExecuteWrite() - "
251 + "device=" + address + ", transId=" + transId
252 + "execWrite=" + execWrite);
253 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800254
Jack Hea355e5e2017-08-22 16:06:54 -0700255 BluetoothDevice device = mAdapter.getRemoteDevice(address);
256 if (device == null) return;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800257
Jack Hea355e5e2017-08-22 16:06:54 -0700258 try {
259 mCallback.onExecuteWrite(device, transId, execWrite);
260 } catch (Exception ex) {
261 Log.w(TAG, "Unhandled exception in callback", ex);
262 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800263 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700264
Jack Hea355e5e2017-08-22 16:06:54 -0700265 /**
266 * A notification/indication has been sent.
267 * @hide
268 */
269 @Override
270 public void onNotificationSent(String address, int status) {
271 if (VDBG) {
272 Log.d(TAG, "onNotificationSent() - "
273 + "device=" + address + ", status=" + status);
274 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700275
Jack Hea355e5e2017-08-22 16:06:54 -0700276 BluetoothDevice device = mAdapter.getRemoteDevice(address);
277 if (device == null) return;
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700278
Jack Hea355e5e2017-08-22 16:06:54 -0700279 try {
280 mCallback.onNotificationSent(device, status);
281 } catch (Exception ex) {
282 Log.w(TAG, "Unhandled exception: " + ex);
283 }
Andre Eisenbachdadefda2014-03-28 14:54:53 -0700284 }
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800285
Jack Hea355e5e2017-08-22 16:06:54 -0700286 /**
287 * The MTU for a connection has changed
288 * @hide
289 */
290 @Override
291 public void onMtuChanged(String address, int mtu) {
292 if (DBG) {
293 Log.d(TAG, "onMtuChanged() - "
294 + "device=" + address + ", mtu=" + mtu);
295 }
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800296
Jack Hea355e5e2017-08-22 16:06:54 -0700297 BluetoothDevice device = mAdapter.getRemoteDevice(address);
298 if (device == null) return;
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800299
Jack Hea355e5e2017-08-22 16:06:54 -0700300 try {
301 mCallback.onMtuChanged(device, mtu);
302 } catch (Exception ex) {
303 Log.w(TAG, "Unhandled exception: " + ex);
304 }
Andre Eisenbach16bf8462014-11-18 11:34:26 -0800305 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800306
Jack Hea355e5e2017-08-22 16:06:54 -0700307 /**
308 * The PHY for a connection was updated
309 * @hide
310 */
311 @Override
312 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
313 if (DBG) {
314 Log.d(TAG,
315 "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
316 + ", rxPHy=" + rxPhy);
317 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800318
Jack Hea355e5e2017-08-22 16:06:54 -0700319 BluetoothDevice device = mAdapter.getRemoteDevice(address);
320 if (device == null) return;
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800321
Jack Hea355e5e2017-08-22 16:06:54 -0700322 try {
323 mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
324 } catch (Exception ex) {
325 Log.w(TAG, "Unhandled exception: " + ex);
326 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800327 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800328
Jack Hea355e5e2017-08-22 16:06:54 -0700329 /**
330 * The PHY for a connection was read
331 * @hide
332 */
333 @Override
334 public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
335 if (DBG) {
336 Log.d(TAG,
337 "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
338 + ", rxPHy=" + rxPhy);
339 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800340
Jack Hea355e5e2017-08-22 16:06:54 -0700341 BluetoothDevice device = mAdapter.getRemoteDevice(address);
342 if (device == null) return;
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800343
Jack Hea355e5e2017-08-22 16:06:54 -0700344 try {
345 mCallback.onPhyRead(device, txPhy, rxPhy, status);
346 } catch (Exception ex) {
347 Log.w(TAG, "Unhandled exception: " + ex);
348 }
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800349 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700350
Jack Hea355e5e2017-08-22 16:06:54 -0700351 /**
352 * Callback invoked when the given connection is updated
353 * @hide
354 */
355 @Override
356 public void onConnectionUpdated(String address, int interval, int latency,
357 int timeout, int status) {
358 if (DBG) {
Jack He2992cd02017-08-22 21:21:23 -0700359 Log.d(TAG, "onConnectionUpdated() - Device=" + address
360 + " interval=" + interval + " latency=" + latency
361 + " timeout=" + timeout + " status=" + status);
Jack Hea355e5e2017-08-22 16:06:54 -0700362 }
363 BluetoothDevice device = mAdapter.getRemoteDevice(address);
364 if (device == null) return;
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700365
Jack Hea355e5e2017-08-22 16:06:54 -0700366 try {
367 mCallback.onConnectionUpdated(device, interval, latency,
368 timeout, status);
369 } catch (Exception ex) {
370 Log.w(TAG, "Unhandled exception: " + ex);
371 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700372 }
Jakub Pawlowski326f7b32017-03-23 19:05:55 -0700373
Jack Hea355e5e2017-08-22 16:06:54 -0700374 };
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800375
376 /**
377 * Create a BluetoothGattServer proxy object.
378 */
Jeremy Kleinadc26ec2016-09-27 14:34:33 -0700379 /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800380 mService = iGatt;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800381 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xieddf7e472013-03-01 18:41:02 -0800382 mCallback = null;
383 mServerIf = 0;
Ganesh Ganapathi Battab88fa822014-04-18 10:00:40 -0700384 mTransport = transport;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800385 mServices = new ArrayList<BluetoothGattService>();
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800386 }
387
388 /**
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700389 * Returns a characteristic with given handle.
Jack Hea355e5e2017-08-22 16:06:54 -0700390 *
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700391 * @hide
392 */
393 /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
Jack Hea355e5e2017-08-22 16:06:54 -0700394 for (BluetoothGattService svc : mServices) {
395 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
396 if (charac.getInstanceId() == handle) {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700397 return charac;
Jack Hea355e5e2017-08-22 16:06:54 -0700398 }
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700399 }
400 }
401 return null;
402 }
403
404 /**
405 * Returns a descriptor with given handle.
Jack Hea355e5e2017-08-22 16:06:54 -0700406 *
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700407 * @hide
408 */
409 /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
Jack Hea355e5e2017-08-22 16:06:54 -0700410 for (BluetoothGattService svc : mServices) {
411 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
412 for (BluetoothGattDescriptor desc : charac.getDescriptors()) {
413 if (desc.getInstanceId() == handle) {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700414 return desc;
Jack Hea355e5e2017-08-22 16:06:54 -0700415 }
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700416 }
417 }
418 }
419 return null;
420 }
421
422 /**
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700423 * Close this GATT server instance.
Matthew Xieb30f91e2013-05-29 10:19:06 -0700424 *
425 * Application should call this method as early as possible after it is done with
426 * this GATT server.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800427 */
Andre Eisenbach3b64f382013-04-05 09:34:11 -0700428 public void close() {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800429 if (DBG) Log.d(TAG, "close()");
Matthew Xieddf7e472013-03-01 18:41:02 -0800430 unregisterCallback();
431 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800432
Matthew Xieddf7e472013-03-01 18:41:02 -0800433 /**
434 * Register an application callback to start using GattServer.
435 *
436 * <p>This is an asynchronous call. The callback is used to notify
437 * success or failure if the function returns true.
438 *
439 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
440 *
Jack Hea355e5e2017-08-22 16:06:54 -0700441 * @param callback GATT callback handler that will receive asynchronous callbacks.
442 * @return true, the callback will be called to notify success or failure, false on immediate
443 * error
Matthew Xieddf7e472013-03-01 18:41:02 -0800444 */
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700445 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800446 if (DBG) Log.d(TAG, "registerCallback()");
447 if (mService == null) {
448 Log.e(TAG, "GATT service not available");
449 return false;
450 }
451 UUID uuid = UUID.randomUUID();
452 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800453
Jack Hea355e5e2017-08-22 16:06:54 -0700454 synchronized (mServerIfLock) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800455 if (mCallback != null) {
456 Log.e(TAG, "App can register callback only once");
457 return false;
458 }
459
460 mCallback = callback;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800461 try {
Matthew Xieddf7e472013-03-01 18:41:02 -0800462 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
463 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700464 Log.e(TAG, "", e);
Matthew Xieddf7e472013-03-01 18:41:02 -0800465 mCallback = null;
466 return false;
467 }
468
469 try {
470 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
471 } catch (InterruptedException e) {
472 Log.e(TAG, "" + e);
473 mCallback = null;
474 }
475
476 if (mServerIf == 0) {
477 mCallback = null;
478 return false;
479 } else {
480 return true;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800481 }
482 }
Matthew Xieddf7e472013-03-01 18:41:02 -0800483 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800484
Matthew Xieddf7e472013-03-01 18:41:02 -0800485 /**
486 * Unregister the current application and callbacks.
487 */
488 private void unregisterCallback() {
489 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
490 if (mService == null || mServerIf == 0) return;
491
492 try {
493 mCallback = null;
494 mService.unregisterServer(mServerIf);
495 mServerIf = 0;
496 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700497 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800498 }
499 }
500
501 /**
502 * Returns a service by UUID, instance and type.
Jack Hea355e5e2017-08-22 16:06:54 -0700503 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800504 * @hide
505 */
506 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
Jack Hea355e5e2017-08-22 16:06:54 -0700507 for (BluetoothGattService svc : mServices) {
Jack He2992cd02017-08-22 21:21:23 -0700508 if (svc.getType() == type
509 && svc.getInstanceId() == instanceId
510 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800511 return svc;
512 }
513 }
514 return null;
515 }
516
517 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800518 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800519 *
520 * <p>The connection may not be established right away, but will be
521 * completed when the remote device is available. A
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700522 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800523 * invoked when the connection state changes as a result of this function.
524 *
525 * <p>The autoConnect paramter determines whether to actively connect to
526 * the remote device, or rather passively scan and finalize the connection
527 * when the remote device is in range/available. Generally, the first ever
528 * connection to a device should be direct (autoConnect set to false) and
529 * subsequent connections to known devices should be invoked with the
Matthew Xieddf7e472013-03-01 18:41:02 -0800530 * autoConnect parameter set to true.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800531 *
532 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
533 *
Jack Hea355e5e2017-08-22 16:06:54 -0700534 * @param autoConnect Whether to directly connect to the remote device (false) or to
535 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800536 * @return true, if the connection attempt was initiated successfully
537 */
538 public boolean connect(BluetoothDevice device, boolean autoConnect) {
Jack Hea355e5e2017-08-22 16:06:54 -0700539 if (DBG) {
540 Log.d(TAG,
541 "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
542 }
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800543 if (mService == null || mServerIf == 0) return false;
544
545 try {
Jack He2992cd02017-08-22 21:21:23 -0700546 // autoConnect is inverse of "isDirect"
547 mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800548 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700549 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800550 return false;
551 }
552
553 return true;
554 }
555
556 /**
557 * Disconnects an established connection, or cancels a connection attempt
558 * currently in progress.
559 *
560 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
561 *
562 * @param device Remote device
563 */
564 public void cancelConnection(BluetoothDevice device) {
565 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
566 if (mService == null || mServerIf == 0) return;
567
568 try {
569 mService.serverDisconnect(mServerIf, device.getAddress());
570 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700571 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800572 }
573 }
574
575 /**
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800576 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowski08ed9242017-03-22 22:44:09 -0700577 * recommendation, whether the PHY change will happen depends on other applications peferences,
Jack Hea355e5e2017-08-22 16:06:54 -0700578 * local and remote controller capabilities. Controller can override these settings. <p> {@link
579 * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if
580 * no PHY change happens. It is also triggered when remote device updates the PHY.
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800581 *
582 * @param device The remote device to send this response to
Jack Hea355e5e2017-08-22 16:06:54 -0700583 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
584 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
585 * BluetoothDevice#PHY_LE_CODED_MASK}.
586 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
587 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
588 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800589 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack Hea355e5e2017-08-22 16:06:54 -0700590 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
591 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800592 */
593 public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
594 try {
595 mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
Jack Hea355e5e2017-08-22 16:06:54 -0700596 phyOptions);
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800597 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700598 Log.e(TAG, "", e);
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800599 }
600 }
601
602 /**
603 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskid64bb882017-03-22 11:22:18 -0700604 * in {@link BluetoothGattServerCallback#onPhyRead}
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800605 *
606 * @param device The remote device to send this response to
607 */
608 public void readPhy(BluetoothDevice device) {
609 try {
610 mService.serverReadPhy(mServerIf, device.getAddress());
611 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700612 Log.e(TAG, "", e);
Jakub Pawlowski409cee62017-02-02 08:07:12 -0800613 }
614 }
615
616 /**
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800617 * Send a response to a read or write request to a remote device.
618 *
619 * <p>This function must be invoked in when a remote read/write request
Matthew Xieddf7e472013-03-01 18:41:02 -0800620 * is received by one of these callback methods:
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800621 *
622 * <ul>
Jack Hea355e5e2017-08-22 16:06:54 -0700623 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
624 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
625 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
626 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800627 * </ul>
628 *
629 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
630 *
631 * @param device The remote device to send this response to
632 * @param requestId The ID of the request that was received with the callback
633 * @param status The status of the request to be sent to the remote devices
634 * @param offset Value offset for partial read/write response
635 * @param value The value of the attribute that was read/written (optional)
636 */
637 public boolean sendResponse(BluetoothDevice device, int requestId,
Jack Hea355e5e2017-08-22 16:06:54 -0700638 int status, int offset, byte[] value) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700639 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800640 if (mService == null || mServerIf == 0) return false;
641
642 try {
643 mService.sendResponse(mServerIf, device.getAddress(), requestId,
Jack Hea355e5e2017-08-22 16:06:54 -0700644 status, offset, value);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800645 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700646 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800647 return false;
648 }
649 return true;
650 }
651
652 /**
653 * Send a notification or indication that a local characteristic has been
654 * updated.
655 *
656 * <p>A notification or indication is sent to the remote device to signal
657 * that the characteristic has been updated. This function should be invoked
658 * for every client that requests notifications/indications by writing
659 * to the "Client Configuration" descriptor for the given characteristic.
660 *
661 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
662 *
663 * @param device The remote device to receive the notification/indication
664 * @param characteristic The local characteristic that has been updated
Jack Hea355e5e2017-08-22 16:06:54 -0700665 * @param confirm true to request confirmation from the client (indication), false to send a
666 * notification
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800667 * @return true, if the notification has been triggered successfully
Jack Hea355e5e2017-08-22 16:06:54 -0700668 * @throws IllegalArgumentException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800669 */
670 public boolean notifyCharacteristicChanged(BluetoothDevice device,
Jack Hea355e5e2017-08-22 16:06:54 -0700671 BluetoothGattCharacteristic characteristic, boolean confirm) {
Andre Eisenbach55d19e42014-07-18 14:38:36 -0700672 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800673 if (mService == null || mServerIf == 0) return false;
674
675 BluetoothGattService service = characteristic.getService();
676 if (service == null) return false;
677
Prerepa Viswanadhamee3cc8b2014-08-13 14:46:58 -0700678 if (characteristic.getValue() == null) {
679 throw new IllegalArgumentException("Chracteristic value is empty. Use "
680 + "BluetoothGattCharacteristic#setvalue to update");
681 }
682
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800683 try {
684 mService.sendNotification(mServerIf, device.getAddress(),
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700685 characteristic.getInstanceId(), confirm,
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800686 characteristic.getValue());
687 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700688 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800689 return false;
690 }
691
692 return true;
693 }
694
695 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800696 * Add a service to the list of services to be hosted.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800697 *
Andre Eisenbach061cfd02014-06-27 14:31:37 -0700698 * <p>Once a service has been addded to the the list, the service and its
Matthew Xieddf7e472013-03-01 18:41:02 -0800699 * included characteristics will be provided by the local device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800700 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800701 * <p>If the local device has already exposed services when this function
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800702 * is called, a service update notification will be sent to all clients.
703 *
Stanley Tng4b5cf482018-04-13 14:54:10 -0700704 * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
705 * whether this service has been added successfully. Do not add another service
706 * before this callback.
707 *
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800708 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
709 *
Jack Hea355e5e2017-08-22 16:06:54 -0700710 * @param service Service to be added to the list of services provided by this device.
Stanley Tng4b5cf482018-04-13 14:54:10 -0700711 * @return true, if the request to add service has been initiated
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800712 */
713 public boolean addService(BluetoothGattService service) {
714 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
715 if (mService == null || mServerIf == 0) return false;
716
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700717 mPendingService = service;
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800718
719 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700720 mService.addService(mServerIf, service);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800721 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700722 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800723 return false;
724 }
725
726 return true;
727 }
728
729 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800730 * Removes a service from the list of services to be provided.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800731 *
732 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
733 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800734 * @param service Service to be removed.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800735 * @return true, if the service has been removed
736 */
737 public boolean removeService(BluetoothGattService service) {
738 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
739 if (mService == null || mServerIf == 0) return false;
740
741 BluetoothGattService intService = getService(service.getUuid(),
Jack Hea355e5e2017-08-22 16:06:54 -0700742 service.getInstanceId(), service.getType());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800743 if (intService == null) return false;
744
745 try {
Jakub Pawlowskid75f5122016-04-01 07:51:45 -0700746 mService.removeService(mServerIf, service.getInstanceId());
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800747 mServices.remove(intService);
748 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700749 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800750 return false;
751 }
752
753 return true;
754 }
755
756 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800757 * Remove all services from the list of provided services.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800758 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
759 */
760 public void clearServices() {
761 if (DBG) Log.d(TAG, "clearServices()");
762 if (mService == null || mServerIf == 0) return;
763
764 try {
765 mService.clearServices(mServerIf);
766 mServices.clear();
767 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700768 Log.e(TAG, "", e);
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800769 }
770 }
771
772 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800773 * Returns a list of GATT services offered by this device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800774 *
775 * <p>An application must call {@link #addService} to add a serice to the
776 * list of services offered by this device.
777 *
778 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
779 *
Jack Hea355e5e2017-08-22 16:06:54 -0700780 * @return List of services. Returns an empty list if no services have been added yet.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800781 */
782 public List<BluetoothGattService> getServices() {
783 return mServices;
784 }
785
786 /**
787 * Returns a {@link BluetoothGattService} from the list of services offered
788 * by this device.
789 *
790 * <p>If multiple instances of the same service (as identified by UUID)
791 * exist, the first instance of the service is returned.
792 *
793 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
794 *
795 * @param uuid UUID of the requested service
Jack Hea355e5e2017-08-22 16:06:54 -0700796 * @return BluetoothGattService if supported, or null if the requested service is not offered by
797 * this device.
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800798 */
799 public BluetoothGattService getService(UUID uuid) {
800 for (BluetoothGattService service : mServices) {
801 if (service.getUuid().equals(uuid)) {
802 return service;
803 }
804 }
805
806 return null;
807 }
808
Matthew Xieddf7e472013-03-01 18:41:02 -0800809
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800810 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800811 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
812 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800813 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800814 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800815 */
816 @Override
817 public int getConnectionState(BluetoothDevice device) {
Matthew Xieddf7e472013-03-01 18:41:02 -0800818 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800819 }
820
821 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800822 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
823 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800824 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800825 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800826 */
827 @Override
828 public List<BluetoothDevice> getConnectedDevices() {
Jack He2992cd02017-08-22 21:21:23 -0700829 throw new UnsupportedOperationException(
830 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800831 }
832
833 /**
Matthew Xieddf7e472013-03-01 18:41:02 -0800834 * Not supported - please use
835 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
836 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800837 *
Matthew Xieddf7e472013-03-01 18:41:02 -0800838 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800839 */
840 @Override
841 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He2992cd02017-08-22 21:21:23 -0700842 throw new UnsupportedOperationException(
843 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta99081122013-02-05 15:28:33 -0800844 }
845}