blob: 088b0169b631bae54d2f97bc094fb3569abad729 [file] [log] [blame]
Ganesh Ganapathi Batta40b98952013-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 Batta40b98952013-02-05 15:28:33 -080019import android.os.ParcelUuid;
20import android.os.RemoteException;
Ganesh Ganapathi Batta40b98952013-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 Xie9afa2742013-03-01 18:41:02 -080028 * Public API for the Bluetooth GATT Profile server role.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080029 *
Matthew Xie9afa2742013-03-01 18:41:02 -080030 * <p>This class provides Bluetooth GATT server role functionality,
Andre Eisenbach25eb5452014-06-27 14:31:37 -070031 * allowing applications to create Bluetooth Smart services and
32 * characteristics.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080033 *
34 * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
Andre Eisenbach25eb5452014-06-27 14:31:37 -070035 * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance
36 * of this class.
Ganesh Ganapathi Batta40b98952013-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 Eisenbach10020cd2014-07-18 14:38:36 -070041 private static final boolean VDBG = false;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080042
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080043 private BluetoothAdapter mAdapter;
44 private IBluetoothGatt mService;
Jakub Pawlowskicd8835e2017-03-22 11:22:18 -070045 private BluetoothGattServerCallback mCallback;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080046
Matthew Xie9afa2742013-03-01 18:41:02 -080047 private Object mServerIfLock = new Object();
48 private int mServerIf;
Ganesh Ganapathi Battaec661912014-04-18 10:00:40 -070049 private int mTransport;
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -070050 private BluetoothGattService mPendingService;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080051 private List<BluetoothGattService> mServices;
52
Matthew Xie9afa2742013-03-01 18:41:02 -080053 private static final int CALLBACK_REG_TIMEOUT = 10000;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -080054
55 /**
56 * Bluetooth GATT interface callbacks
57 */
Jakub Pawlowskiddf66d32017-03-27 12:14:40 -070058 private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
Jack He910201b2017-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 Xie9afa2742013-03-01 18:41:02 -080069 }
Jack He910201b2017-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 Pawlowski8d7adf22016-04-01 07:51:45 -070078 }
79 }
80
Jack He910201b2017-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 Pawlowski8d7adf22016-04-01 07:51:45 -070099 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800100
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800111
Jack He910201b2017-08-22 16:06:54 -0700112 if (mPendingService == null) {
113 return;
114 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800115
Jack He910201b2017-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 Pawlowski8d7adf22016-04-01 07:51:45 -0700143 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800144
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800153
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800160
Jack He910201b2017-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 Pawlowski8d7adf22016-04-01 07:51:45 -0700167 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800168
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800190 }
191
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800201
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800208
Jack He910201b2017-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 Pawlowski8d7adf22016-04-01 07:51:45 -0700216 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800217
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800240 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800241
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800254
Jack He910201b2017-08-22 16:06:54 -0700255 BluetoothDevice device = mAdapter.getRemoteDevice(address);
256 if (device == null) return;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800257
Jack He910201b2017-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 Batta40b98952013-02-05 15:28:33 -0800263 }
Andre Eisenbach745ef9e2014-03-28 14:54:53 -0700264
Jack He910201b2017-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 Eisenbach745ef9e2014-03-28 14:54:53 -0700275
Jack He910201b2017-08-22 16:06:54 -0700276 BluetoothDevice device = mAdapter.getRemoteDevice(address);
277 if (device == null) return;
Andre Eisenbach745ef9e2014-03-28 14:54:53 -0700278
Jack He910201b2017-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 Eisenbach745ef9e2014-03-28 14:54:53 -0700284 }
Andre Eisenbach5682be92014-11-18 11:34:26 -0800285
Jack He910201b2017-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 Eisenbach5682be92014-11-18 11:34:26 -0800296
Jack He910201b2017-08-22 16:06:54 -0700297 BluetoothDevice device = mAdapter.getRemoteDevice(address);
298 if (device == null) return;
Andre Eisenbach5682be92014-11-18 11:34:26 -0800299
Jack He910201b2017-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 Eisenbach5682be92014-11-18 11:34:26 -0800305 }
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800306
Jack He910201b2017-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 Pawlowskiad091bb2017-02-02 08:07:12 -0800318
Jack He910201b2017-08-22 16:06:54 -0700319 BluetoothDevice device = mAdapter.getRemoteDevice(address);
320 if (device == null) return;
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800321
Jack He910201b2017-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 Pawlowskiad091bb2017-02-02 08:07:12 -0800327 }
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800328
Jack He910201b2017-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 Pawlowskiad091bb2017-02-02 08:07:12 -0800340
Jack He910201b2017-08-22 16:06:54 -0700341 BluetoothDevice device = mAdapter.getRemoteDevice(address);
342 if (device == null) return;
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800343
Jack He910201b2017-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 Pawlowskiad091bb2017-02-02 08:07:12 -0800349 }
Jakub Pawlowskib651de22017-03-23 19:05:55 -0700350
Jack He910201b2017-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 He9e045d22017-08-22 21:21:23 -0700359 Log.d(TAG, "onConnectionUpdated() - Device=" + address
360 + " interval=" + interval + " latency=" + latency
361 + " timeout=" + timeout + " status=" + status);
Jack He910201b2017-08-22 16:06:54 -0700362 }
363 BluetoothDevice device = mAdapter.getRemoteDevice(address);
364 if (device == null) return;
Jakub Pawlowskib651de22017-03-23 19:05:55 -0700365
Jack He910201b2017-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 Pawlowskib651de22017-03-23 19:05:55 -0700372 }
Jakub Pawlowskib651de22017-03-23 19:05:55 -0700373
Jack He910201b2017-08-22 16:06:54 -0700374 };
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800375
376 /**
377 * Create a BluetoothGattServer proxy object.
378 */
Jeremy Klein59d15652016-09-27 14:34:33 -0700379 /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) {
Matthew Xie9afa2742013-03-01 18:41:02 -0800380 mService = iGatt;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800381 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xie9afa2742013-03-01 18:41:02 -0800382 mCallback = null;
383 mServerIf = 0;
Ganesh Ganapathi Battaec661912014-04-18 10:00:40 -0700384 mTransport = transport;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800385 mServices = new ArrayList<BluetoothGattService>();
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800386 }
387
388 /**
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700389 * Returns a characteristic with given handle.
Jack He910201b2017-08-22 16:06:54 -0700390 *
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700391 * @hide
392 */
393 /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) {
Jack He910201b2017-08-22 16:06:54 -0700394 for (BluetoothGattService svc : mServices) {
395 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
396 if (charac.getInstanceId() == handle) {
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700397 return charac;
Jack He910201b2017-08-22 16:06:54 -0700398 }
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700399 }
400 }
401 return null;
402 }
403
404 /**
405 * Returns a descriptor with given handle.
Jack He910201b2017-08-22 16:06:54 -0700406 *
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700407 * @hide
408 */
409 /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) {
Jack He910201b2017-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 Pawlowski8d7adf22016-04-01 07:51:45 -0700414 return desc;
Jack He910201b2017-08-22 16:06:54 -0700415 }
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700416 }
417 }
418 }
419 return null;
420 }
421
422 /**
Andre Eisenbach6b79dec2013-04-05 09:34:11 -0700423 * Close this GATT server instance.
Matthew Xieabb65582013-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 Batta40b98952013-02-05 15:28:33 -0800427 */
Andre Eisenbach6b79dec2013-04-05 09:34:11 -0700428 public void close() {
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800429 if (DBG) Log.d(TAG, "close()");
Matthew Xie9afa2742013-03-01 18:41:02 -0800430 unregisterCallback();
431 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800432
Matthew Xie9afa2742013-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 He910201b2017-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 Xie9afa2742013-03-01 18:41:02 -0800444 */
Jakub Pawlowskicd8835e2017-03-22 11:22:18 -0700445 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
Łukasz Rymanowski5d556452020-07-23 14:57:36 +0200446 return registerCallback(callback, false);
447 }
448
449 /**
450 * Register an application callback to start using GattServer.
451 *
452 * <p>This is an asynchronous call. The callback is used to notify
453 * success or failure if the function returns true.
454 *
455 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
456 *
457 * @param callback GATT callback handler that will receive asynchronous callbacks.
458 * @param eatt_support indicates if server can use eatt
459 * @return true, the callback will be called to notify success or failure, false on immediate
460 * error
461 * @hide
462 */
463 /*package*/ boolean registerCallback(BluetoothGattServerCallback callback,
464 boolean eatt_support) {
Matthew Xie9afa2742013-03-01 18:41:02 -0800465 if (DBG) Log.d(TAG, "registerCallback()");
466 if (mService == null) {
467 Log.e(TAG, "GATT service not available");
468 return false;
469 }
470 UUID uuid = UUID.randomUUID();
471 if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800472
Jack He910201b2017-08-22 16:06:54 -0700473 synchronized (mServerIfLock) {
Matthew Xie9afa2742013-03-01 18:41:02 -0800474 if (mCallback != null) {
475 Log.e(TAG, "App can register callback only once");
476 return false;
477 }
478
479 mCallback = callback;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800480 try {
Łukasz Rymanowski5d556452020-07-23 14:57:36 +0200481 mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support);
Matthew Xie9afa2742013-03-01 18:41:02 -0800482 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700483 Log.e(TAG, "", e);
Matthew Xie9afa2742013-03-01 18:41:02 -0800484 mCallback = null;
485 return false;
486 }
487
488 try {
489 mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
490 } catch (InterruptedException e) {
491 Log.e(TAG, "" + e);
492 mCallback = null;
493 }
494
495 if (mServerIf == 0) {
496 mCallback = null;
497 return false;
498 } else {
499 return true;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800500 }
501 }
Matthew Xie9afa2742013-03-01 18:41:02 -0800502 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800503
Matthew Xie9afa2742013-03-01 18:41:02 -0800504 /**
505 * Unregister the current application and callbacks.
506 */
507 private void unregisterCallback() {
508 if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
509 if (mService == null || mServerIf == 0) return;
510
511 try {
512 mCallback = null;
513 mService.unregisterServer(mServerIf);
514 mServerIf = 0;
515 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700516 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800517 }
518 }
519
520 /**
521 * Returns a service by UUID, instance and type.
Jack He910201b2017-08-22 16:06:54 -0700522 *
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800523 * @hide
524 */
525 /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) {
Jack He910201b2017-08-22 16:06:54 -0700526 for (BluetoothGattService svc : mServices) {
Jack He9e045d22017-08-22 21:21:23 -0700527 if (svc.getType() == type
528 && svc.getInstanceId() == instanceId
529 && svc.getUuid().equals(uuid)) {
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800530 return svc;
531 }
532 }
533 return null;
534 }
535
536 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800537 * Initiate a connection to a Bluetooth GATT capable device.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800538 *
539 * <p>The connection may not be established right away, but will be
540 * completed when the remote device is available. A
Jakub Pawlowskicd8835e2017-03-22 11:22:18 -0700541 * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800542 * invoked when the connection state changes as a result of this function.
543 *
kopriva985ec792018-10-09 13:42:28 -0700544 * <p>The autoConnect parameter determines whether to actively connect to
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800545 * the remote device, or rather passively scan and finalize the connection
546 * when the remote device is in range/available. Generally, the first ever
547 * connection to a device should be direct (autoConnect set to false) and
548 * subsequent connections to known devices should be invoked with the
Matthew Xie9afa2742013-03-01 18:41:02 -0800549 * autoConnect parameter set to true.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800550 *
551 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
552 *
Jack He910201b2017-08-22 16:06:54 -0700553 * @param autoConnect Whether to directly connect to the remote device (false) or to
554 * automatically connect as soon as the remote device becomes available (true).
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800555 * @return true, if the connection attempt was initiated successfully
556 */
557 public boolean connect(BluetoothDevice device, boolean autoConnect) {
Jack He910201b2017-08-22 16:06:54 -0700558 if (DBG) {
559 Log.d(TAG,
560 "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
561 }
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800562 if (mService == null || mServerIf == 0) return false;
563
564 try {
Jack He9e045d22017-08-22 21:21:23 -0700565 // autoConnect is inverse of "isDirect"
566 mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800567 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700568 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800569 return false;
570 }
571
572 return true;
573 }
574
575 /**
576 * Disconnects an established connection, or cancels a connection attempt
577 * currently in progress.
578 *
579 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
580 *
581 * @param device Remote device
582 */
583 public void cancelConnection(BluetoothDevice device) {
584 if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
585 if (mService == null || mServerIf == 0) return;
586
587 try {
588 mService.serverDisconnect(mServerIf, device.getAddress());
589 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700590 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800591 }
592 }
593
594 /**
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800595 * Set the preferred connection PHY for this app. Please note that this is just a
Jakub Pawlowski1bafa472017-03-22 22:44:09 -0700596 * recommendation, whether the PHY change will happen depends on other applications peferences,
Jack He910201b2017-08-22 16:06:54 -0700597 * local and remote controller capabilities. Controller can override these settings. <p> {@link
598 * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if
599 * no PHY change happens. It is also triggered when remote device updates the PHY.
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800600 *
601 * @param device The remote device to send this response to
Jack He910201b2017-08-22 16:06:54 -0700602 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link
603 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
604 * BluetoothDevice#PHY_LE_CODED_MASK}.
605 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link
606 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link
607 * BluetoothDevice#PHY_LE_CODED_MASK}.
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800608 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
Jack He910201b2017-08-22 16:06:54 -0700609 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
610 * {@link BluetoothDevice#PHY_OPTION_S8}
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800611 */
612 public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
613 try {
614 mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
Jack He910201b2017-08-22 16:06:54 -0700615 phyOptions);
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800616 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700617 Log.e(TAG, "", e);
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800618 }
619 }
620
621 /**
622 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
Jakub Pawlowskicd8835e2017-03-22 11:22:18 -0700623 * in {@link BluetoothGattServerCallback#onPhyRead}
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800624 *
625 * @param device The remote device to send this response to
626 */
627 public void readPhy(BluetoothDevice device) {
628 try {
629 mService.serverReadPhy(mServerIf, device.getAddress());
630 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700631 Log.e(TAG, "", e);
Jakub Pawlowskiad091bb2017-02-02 08:07:12 -0800632 }
633 }
634
635 /**
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800636 * Send a response to a read or write request to a remote device.
637 *
638 * <p>This function must be invoked in when a remote read/write request
Matthew Xie9afa2742013-03-01 18:41:02 -0800639 * is received by one of these callback methods:
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800640 *
641 * <ul>
Jack He910201b2017-08-22 16:06:54 -0700642 * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
643 * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
644 * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
645 * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800646 * </ul>
647 *
648 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
649 *
650 * @param device The remote device to send this response to
651 * @param requestId The ID of the request that was received with the callback
652 * @param status The status of the request to be sent to the remote devices
653 * @param offset Value offset for partial read/write response
654 * @param value The value of the attribute that was read/written (optional)
655 */
656 public boolean sendResponse(BluetoothDevice device, int requestId,
Jack He910201b2017-08-22 16:06:54 -0700657 int status, int offset, byte[] value) {
Andre Eisenbach10020cd2014-07-18 14:38:36 -0700658 if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800659 if (mService == null || mServerIf == 0) return false;
660
661 try {
662 mService.sendResponse(mServerIf, device.getAddress(), requestId,
Jack He910201b2017-08-22 16:06:54 -0700663 status, offset, value);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800664 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700665 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800666 return false;
667 }
668 return true;
669 }
670
671 /**
672 * Send a notification or indication that a local characteristic has been
673 * updated.
674 *
675 * <p>A notification or indication is sent to the remote device to signal
676 * that the characteristic has been updated. This function should be invoked
677 * for every client that requests notifications/indications by writing
678 * to the "Client Configuration" descriptor for the given characteristic.
679 *
680 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
681 *
682 * @param device The remote device to receive the notification/indication
683 * @param characteristic The local characteristic that has been updated
Jack He910201b2017-08-22 16:06:54 -0700684 * @param confirm true to request confirmation from the client (indication), false to send a
685 * notification
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800686 * @return true, if the notification has been triggered successfully
Jack He910201b2017-08-22 16:06:54 -0700687 * @throws IllegalArgumentException
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800688 */
689 public boolean notifyCharacteristicChanged(BluetoothDevice device,
Jack He910201b2017-08-22 16:06:54 -0700690 BluetoothGattCharacteristic characteristic, boolean confirm) {
Andre Eisenbach10020cd2014-07-18 14:38:36 -0700691 if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800692 if (mService == null || mServerIf == 0) return false;
693
694 BluetoothGattService service = characteristic.getService();
695 if (service == null) return false;
696
Prerepa Viswanadhamffabc512014-08-13 14:46:58 -0700697 if (characteristic.getValue() == null) {
698 throw new IllegalArgumentException("Chracteristic value is empty. Use "
699 + "BluetoothGattCharacteristic#setvalue to update");
700 }
701
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800702 try {
703 mService.sendNotification(mServerIf, device.getAddress(),
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700704 characteristic.getInstanceId(), confirm,
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800705 characteristic.getValue());
706 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700707 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800708 return false;
709 }
710
711 return true;
712 }
713
714 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800715 * Add a service to the list of services to be hosted.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800716 *
kopriva985ec792018-10-09 13:42:28 -0700717 * <p>Once a service has been addded to the list, the service and its
Matthew Xie9afa2742013-03-01 18:41:02 -0800718 * included characteristics will be provided by the local device.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800719 *
Matthew Xie9afa2742013-03-01 18:41:02 -0800720 * <p>If the local device has already exposed services when this function
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800721 * is called, a service update notification will be sent to all clients.
722 *
Stanley Tng9b60fc72018-04-13 14:54:10 -0700723 * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate
724 * whether this service has been added successfully. Do not add another service
725 * before this callback.
726 *
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800727 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
728 *
Jack He910201b2017-08-22 16:06:54 -0700729 * @param service Service to be added to the list of services provided by this device.
Stanley Tng9b60fc72018-04-13 14:54:10 -0700730 * @return true, if the request to add service has been initiated
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800731 */
732 public boolean addService(BluetoothGattService service) {
733 if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
734 if (mService == null || mServerIf == 0) return false;
735
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700736 mPendingService = service;
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800737
738 try {
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700739 mService.addService(mServerIf, service);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800740 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700741 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800742 return false;
743 }
744
745 return true;
746 }
747
748 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800749 * Removes a service from the list of services to be provided.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800750 *
751 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
752 *
Matthew Xie9afa2742013-03-01 18:41:02 -0800753 * @param service Service to be removed.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800754 * @return true, if the service has been removed
755 */
756 public boolean removeService(BluetoothGattService service) {
757 if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
758 if (mService == null || mServerIf == 0) return false;
759
760 BluetoothGattService intService = getService(service.getUuid(),
Jack He910201b2017-08-22 16:06:54 -0700761 service.getInstanceId(), service.getType());
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800762 if (intService == null) return false;
763
764 try {
Jakub Pawlowski8d7adf22016-04-01 07:51:45 -0700765 mService.removeService(mServerIf, service.getInstanceId());
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800766 mServices.remove(intService);
767 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700768 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800769 return false;
770 }
771
772 return true;
773 }
774
775 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800776 * Remove all services from the list of provided services.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800777 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
778 */
779 public void clearServices() {
780 if (DBG) Log.d(TAG, "clearServices()");
781 if (mService == null || mServerIf == 0) return;
782
783 try {
784 mService.clearServices(mServerIf);
785 mServices.clear();
786 } catch (RemoteException e) {
Jack He910201b2017-08-22 16:06:54 -0700787 Log.e(TAG, "", e);
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800788 }
789 }
790
791 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800792 * Returns a list of GATT services offered by this device.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800793 *
794 * <p>An application must call {@link #addService} to add a serice to the
795 * list of services offered by this device.
796 *
797 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
798 *
Jack He910201b2017-08-22 16:06:54 -0700799 * @return List of services. Returns an empty list if no services have been added yet.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800800 */
801 public List<BluetoothGattService> getServices() {
802 return mServices;
803 }
804
805 /**
806 * Returns a {@link BluetoothGattService} from the list of services offered
807 * by this device.
808 *
809 * <p>If multiple instances of the same service (as identified by UUID)
810 * exist, the first instance of the service is returned.
811 *
812 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
813 *
814 * @param uuid UUID of the requested service
Jack He910201b2017-08-22 16:06:54 -0700815 * @return BluetoothGattService if supported, or null if the requested service is not offered by
816 * this device.
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800817 */
818 public BluetoothGattService getService(UUID uuid) {
819 for (BluetoothGattService service : mServices) {
820 if (service.getUuid().equals(uuid)) {
821 return service;
822 }
823 }
824
825 return null;
826 }
827
Matthew Xie9afa2742013-03-01 18:41:02 -0800828
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800829 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800830 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
831 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800832 *
Matthew Xie9afa2742013-03-01 18:41:02 -0800833 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800834 */
835 @Override
836 public int getConnectionState(BluetoothDevice device) {
Matthew Xie9afa2742013-03-01 18:41:02 -0800837 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800838 }
839
840 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800841 * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
842 * with {@link BluetoothProfile#GATT} as argument
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800843 *
Matthew Xie9afa2742013-03-01 18:41:02 -0800844 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800845 */
846 @Override
847 public List<BluetoothDevice> getConnectedDevices() {
Jack He9e045d22017-08-22 21:21:23 -0700848 throw new UnsupportedOperationException(
849 "Use BluetoothManager#getConnectedDevices instead.");
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800850 }
851
852 /**
Matthew Xie9afa2742013-03-01 18:41:02 -0800853 * Not supported - please use
854 * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
855 * with {@link BluetoothProfile#GATT} as first argument
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800856 *
Matthew Xie9afa2742013-03-01 18:41:02 -0800857 * @throws UnsupportedOperationException
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800858 */
859 @Override
860 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He9e045d22017-08-22 21:21:23 -0700861 throw new UnsupportedOperationException(
862 "Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
Ganesh Ganapathi Batta40b98952013-02-05 15:28:33 -0800863 }
864}