1 /*
2  * Copyright (C) 2019 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 
17 package com.android.car.trust;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothGattCharacteristic;
24 import android.bluetooth.BluetoothGattDescriptor;
25 import android.bluetooth.BluetoothGattService;
26 import android.bluetooth.le.AdvertiseCallback;
27 import android.bluetooth.le.AdvertiseData;
28 import android.bluetooth.le.AdvertiseSettings;
29 import android.content.Context;
30 import android.os.Handler;
31 import android.os.ParcelUuid;
32 import android.util.Log;
33 
34 import androidx.collection.SimpleArrayMap;
35 
36 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
37 import com.android.car.BLEStreamProtos.VersionExchangeProto.BLEVersionExchange;
38 import com.android.car.R;
39 import com.android.car.Utils;
40 import com.android.car.protobuf.InvalidProtocolBufferException;
41 import com.android.internal.annotations.VisibleForTesting;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.UUID;
48 import java.util.concurrent.TimeUnit;
49 
50 /**
51  * A BLE Service that is used for communicating with the trusted peer device. This extends from a
52  * more generic {@link BlePeripheralManager} and has more context on the BLE requirements for the
53  * Trusted device feature. It has knowledge on the GATT services and characteristics that are
54  * specific to the Trusted Device feature.
55  */
56 class CarTrustAgentBleManager implements BleMessageStreamCallback, BlePeripheralManager.Callback,
57         BlePeripheralManager.OnCharacteristicWriteListener {
58     private static final String TAG = "CarTrustBLEManager";
59 
60     /**
61      * The UUID of the Client Characteristic Configuration Descriptor. This descriptor is
62      * responsible for specifying if a characteristic can be subscribed to for notifications.
63      *
64      * @see <a href="https://www.bluetooth.com/specifications/gatt/descriptors/">
65      * GATT Descriptors</a>
66      */
67     private static final UUID CLIENT_CHARACTERISTIC_CONFIG =
68             UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
69 
70     /**
71      * Reserved bytes for an ATT write request payload.
72      *
73      * <p>The attribute protocol uses 3 bytes to encode the command type and attribute ID. These
74      * bytes need to be subtracted from the reported MTU size and the resulting value will
75      * represent the total amount of bytes that can be sent in a write.
76      */
77     private static final int ATT_PAYLOAD_RESERVED_BYTES = 3;
78 
79     /** @hide */
80     @IntDef(prefix = {"TRUSTED_DEVICE_OPERATION_"}, value = {
81             TRUSTED_DEVICE_OPERATION_NONE,
82             TRUSTED_DEVICE_OPERATION_ENROLLMENT,
83             TRUSTED_DEVICE_OPERATION_UNLOCK
84     })
85     @Retention(RetentionPolicy.SOURCE)
86     public @interface TrustedDeviceOperation {
87     }
88 
89     private static final int TRUSTED_DEVICE_OPERATION_NONE = 0;
90     private static final int TRUSTED_DEVICE_OPERATION_ENROLLMENT = 1;
91     private static final int TRUSTED_DEVICE_OPERATION_UNLOCK = 2;
92     @VisibleForTesting
93     static final long BLE_MESSAGE_RETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(2);
94 
95     private final Context mContext;
96     private final BlePeripheralManager mBlePeripheralManager;
97 
98     @TrustedDeviceOperation
99     private int mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_NONE;
100     private String mOriginalBluetoothName;
101     private byte[] mUniqueId;
102 
103     /**
104      * The maximum amount of bytes that can be written over BLE.
105      *
106      * <p>This initial value is 20 because BLE has a default write of 23 bytes. However, 3 bytes
107      * are subtracted due to bytes being reserved for the command type and attribute ID.
108      *
109      * @see #ATT_PAYLOAD_RESERVED_BYTES
110      */
111     private int mMaxWriteSize = 20;
112 
113     @VisibleForTesting
114     int mBleMessageRetryLimit = 20;
115 
116     private final List<BleEventCallback> mBleEventCallbacks = new ArrayList<>();
117     private AdvertiseCallback mEnrollmentAdvertisingCallback;
118     private SendMessageCallback mSendMessageCallback;
119 
120     // Enrollment Service and Characteristic UUIDs
121     private UUID mEnrollmentServiceUuid;
122     private UUID mEnrollmentClientWriteUuid;
123     private UUID mEnrollmentServerWriteUuid;
124     private BluetoothGattService mEnrollmentGattService;
125 
126     // Unlock Service and Characteristic UUIDs
127     private UUID mUnlockServiceUuid;
128     private UUID mUnlockClientWriteUuid;
129     private UUID mUnlockServerWriteUuid;
130     private BluetoothGattService mUnlockGattService;
131 
132     @Nullable
133     private BleMessageStream mMessageStream;
134     private final Handler mHandler = new Handler();
135 
136     // A map of enrollment/unlock client write uuid -> listener
137     private final SimpleArrayMap<UUID, DataReceivedListener> mDataReceivedListeners =
138             new SimpleArrayMap<>();
139 
CarTrustAgentBleManager(Context context, BlePeripheralManager blePeripheralManager)140     CarTrustAgentBleManager(Context context, BlePeripheralManager blePeripheralManager) {
141         mContext = context;
142         mBlePeripheralManager = blePeripheralManager;
143         mBlePeripheralManager.registerCallback(this);
144     }
145 
146     /**
147      * This should be called before starting unlock advertising
148      */
setUniqueId(UUID uniqueId)149     void setUniqueId(UUID uniqueId) {
150         mUniqueId = Utils.uuidToBytes(uniqueId);
151     }
152 
cleanup()153     void cleanup() {
154         mBlePeripheralManager.cleanup();
155     }
156 
stopGattServer()157     void stopGattServer() {
158         mBlePeripheralManager.stopGattServer();
159     }
160 
161     // Overriding some of the {@link BLEManager} methods to be specific for Trusted Device feature.
162     @Override
onRemoteDeviceConnected(BluetoothDevice device)163     public void onRemoteDeviceConnected(BluetoothDevice device) {
164         if (mBleEventCallbacks.isEmpty()) {
165             Log.e(TAG, "No valid BleEventCallback for trust device.");
166             return;
167         }
168 
169         // Retrieving device name only happens in enrollment, the retrieved device name will be
170         // stored in sharedPreference for further use.
171         if (mCurrentTrustedDeviceOperation == TRUSTED_DEVICE_OPERATION_ENROLLMENT
172                 && device.getName() == null) {
173             mBlePeripheralManager.retrieveDeviceName(device);
174         }
175 
176         if (mMessageStream != null) {
177             mMessageStream.unregisterCallback(this);
178             mMessageStream = null;
179         }
180 
181         mSendMessageCallback = null;
182 
183         mBlePeripheralManager.addOnCharacteristicWriteListener(this);
184         mBleEventCallbacks.forEach(bleEventCallback ->
185                 bleEventCallback.onRemoteDeviceConnected(device));
186     }
187 
188     @Override
onRemoteDeviceDisconnected(BluetoothDevice device)189     public void onRemoteDeviceDisconnected(BluetoothDevice device) {
190         mBlePeripheralManager.removeOnCharacteristicWriteListener(this);
191         mBleEventCallbacks.forEach(bleEventCallback ->
192                 bleEventCallback.onRemoteDeviceDisconnected(device));
193 
194         if (mMessageStream != null) {
195             mMessageStream.unregisterCallback(this);
196             mMessageStream = null;
197         }
198 
199         mSendMessageCallback = null;
200     }
201 
202     @Override
onDeviceNameRetrieved(@ullable String deviceName)203     public void onDeviceNameRetrieved(@Nullable String deviceName) {
204         mBleEventCallbacks.forEach(bleEventCallback ->
205                 bleEventCallback.onClientDeviceNameRetrieved(deviceName));
206     }
207 
208     @Override
onMtuSizeChanged(int size)209     public void onMtuSizeChanged(int size) {
210         mMaxWriteSize = size - ATT_PAYLOAD_RESERVED_BYTES;
211 
212         if (mMessageStream != null) {
213             mMessageStream.setMaxWriteSize(mMaxWriteSize);
214         }
215 
216         if (Log.isLoggable(TAG, Log.DEBUG)) {
217             Log.d(TAG, "MTU size changed to: " + size
218                     + "; setting max payload size to: " + mMaxWriteSize);
219         }
220     }
221 
222     @Override
onCharacteristicWrite(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value)223     public void onCharacteristicWrite(BluetoothDevice device,
224             BluetoothGattCharacteristic characteristic, byte[] value) {
225         UUID uuid = characteristic.getUuid();
226         if (Log.isLoggable(TAG, Log.DEBUG)) {
227             Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid);
228         }
229 
230         if (mMessageStream == null) {
231             resolveBLEVersion(device, value, uuid);
232             return;
233         }
234 
235         Log.e(TAG, "Received a message but message stream has already been created. "
236                 + "Was this manager not unregistered as a listener for writes?");
237     }
238 
239     @VisibleForTesting
setBleMessageRetryLimit(int limit)240     void setBleMessageRetryLimit(int limit) {
241         mBleMessageRetryLimit = limit;
242     }
243 
resolveBLEVersion(BluetoothDevice device, byte[] value, UUID clientCharacteristicUUID)244     private void resolveBLEVersion(BluetoothDevice device, byte[] value,
245             UUID clientCharacteristicUUID) {
246         BluetoothGattCharacteristic writeCharacteristic =
247                 getCharacteristicForWrite(clientCharacteristicUUID);
248 
249         if (writeCharacteristic == null) {
250             Log.e(TAG, "Invalid write UUID (" + clientCharacteristicUUID
251                     + ") during version exchange; disconnecting from remote device.");
252             disconnectRemoteDevice();
253             return;
254         }
255 
256         BluetoothGattCharacteristic readCharacteristic =
257                 clientCharacteristicUUID.equals(mEnrollmentClientWriteUuid)
258                         ? mEnrollmentGattService.getCharacteristic(clientCharacteristicUUID)
259                         : mUnlockGattService.getCharacteristic(clientCharacteristicUUID);
260 
261         // If this occurs, then there is a bug in the retrieval code above.
262         if (readCharacteristic == null) {
263             Log.e(TAG, "No read characteristic corresponding to UUID ("
264                     + clientCharacteristicUUID + "). Cannot listen for messages. Disconnecting.");
265             disconnectRemoteDevice();
266             return;
267         }
268 
269         BLEVersionExchange deviceVersion;
270         try {
271             deviceVersion = BLEVersionExchange.parseFrom(value);
272         } catch (InvalidProtocolBufferException e) {
273             disconnectRemoteDevice();
274             Log.e(TAG, "Could not parse version exchange message", e);
275             return;
276         }
277 
278         mMessageStream = BLEVersionExchangeResolver.resolveToStream(
279                 deviceVersion, device, mBlePeripheralManager, writeCharacteristic,
280                 readCharacteristic);
281         mMessageStream.setMaxWriteSize(mMaxWriteSize);
282         mMessageStream.registerCallback(this);
283 
284         if (mMessageStream == null) {
285             Log.e(TAG, "No supported version found during version exchange. "
286                     +  "Could not create message stream.");
287             disconnectRemoteDevice();
288             return;
289         }
290 
291         // No need for this manager to listen for any writes; the stream will handle that from now
292         // on.
293         mBlePeripheralManager.removeOnCharacteristicWriteListener(this);
294 
295         // The message stream is not used to send the IHU's version, but will be used for
296         // any subsequent messages.
297         BLEVersionExchange headunitVersion = BLEVersionExchangeResolver.makeVersionExchange();
298         setValueOnCharacteristicAndNotify(device, headunitVersion.toByteArray(),
299                 writeCharacteristic);
300 
301         if (Log.isLoggable(TAG, Log.DEBUG)) {
302             Log.d(TAG, "Sent supported version to the phone.");
303         }
304     }
305 
306     /**
307      * Setup the BLE GATT server for Enrollment. The GATT server for Enrollment comprises of one
308      * GATT Service and 2 characteristics - one for the phone to write to and one for the head unit
309      * to write to.
310      */
setupEnrollmentBleServer()311     void setupEnrollmentBleServer() {
312         mEnrollmentServiceUuid = UUID.fromString(
313                 mContext.getString(R.string.enrollment_service_uuid));
314         mEnrollmentClientWriteUuid = UUID.fromString(
315                 mContext.getString(R.string.enrollment_client_write_uuid));
316         mEnrollmentServerWriteUuid = UUID.fromString(
317                 mContext.getString(R.string.enrollment_server_write_uuid));
318 
319         mEnrollmentGattService = new BluetoothGattService(mEnrollmentServiceUuid,
320                 BluetoothGattService.SERVICE_TYPE_PRIMARY);
321 
322         // Characteristic the connected bluetooth device will write to.
323         BluetoothGattCharacteristic clientCharacteristic =
324                 new BluetoothGattCharacteristic(mEnrollmentClientWriteUuid,
325                         BluetoothGattCharacteristic.PROPERTY_WRITE
326                                 | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
327                         BluetoothGattCharacteristic.PERMISSION_WRITE);
328 
329         // Characteristic that this manager will write to.
330         BluetoothGattCharacteristic serverCharacteristic =
331                 new BluetoothGattCharacteristic(mEnrollmentServerWriteUuid,
332                         BluetoothGattCharacteristic.PROPERTY_NOTIFY,
333                         BluetoothGattCharacteristic.PERMISSION_READ);
334 
335         addDescriptorToCharacteristic(serverCharacteristic);
336 
337         mEnrollmentGattService.addCharacteristic(clientCharacteristic);
338         mEnrollmentGattService.addCharacteristic(serverCharacteristic);
339     }
340 
341     /**
342      * Setup the BLE GATT server for Unlocking the Head unit. The GATT server for this phase also
343      * comprises of 1 Service and 2 characteristics. However both the token and the handle are sent
344      * from the phone to the head unit.
345      */
setupUnlockBleServer()346     void setupUnlockBleServer() {
347         mUnlockServiceUuid = UUID.fromString(mContext.getString(R.string.unlock_service_uuid));
348         mUnlockClientWriteUuid = UUID
349                 .fromString(mContext.getString(R.string.unlock_client_write_uuid));
350         mUnlockServerWriteUuid = UUID
351                 .fromString(mContext.getString(R.string.unlock_server_write_uuid));
352 
353         mUnlockGattService = new BluetoothGattService(mUnlockServiceUuid,
354                 BluetoothGattService.SERVICE_TYPE_PRIMARY);
355 
356         // Characteristic the connected bluetooth device will write to.
357         BluetoothGattCharacteristic clientCharacteristic = new BluetoothGattCharacteristic(
358                 mUnlockClientWriteUuid,
359                 BluetoothGattCharacteristic.PROPERTY_WRITE
360                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
361                 BluetoothGattCharacteristic.PERMISSION_WRITE);
362 
363         // Characteristic that this manager will write to.
364         BluetoothGattCharacteristic serverCharacteristic = new BluetoothGattCharacteristic(
365                 mUnlockServerWriteUuid,
366                 BluetoothGattCharacteristic.PROPERTY_NOTIFY,
367                 BluetoothGattCharacteristic.PERMISSION_READ);
368 
369         addDescriptorToCharacteristic(serverCharacteristic);
370 
371         mUnlockGattService.addCharacteristic(clientCharacteristic);
372         mUnlockGattService.addCharacteristic(serverCharacteristic);
373     }
374 
375     @Override
onMessageReceivedError(UUID uuid)376     public void onMessageReceivedError(UUID uuid) {
377         Log.e(TAG, "Error parsing the message from the client on UUID: " + uuid);
378     }
379 
380     @Override
onMessageReceived(byte[] message, UUID uuid)381     public void onMessageReceived(byte[] message, UUID uuid) {
382         if (mDataReceivedListeners.containsKey(uuid)) {
383             mDataReceivedListeners.get(uuid).onDataReceived(message);
384         }
385     }
386 
387     @Override
onWriteMessageError()388     public void onWriteMessageError() {
389         if (mSendMessageCallback != null) {
390             mSendMessageCallback.onSendMessageFailure();
391         }
392     }
393 
addDescriptorToCharacteristic(BluetoothGattCharacteristic characteristic)394     private void addDescriptorToCharacteristic(BluetoothGattCharacteristic characteristic) {
395         BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(
396                 CLIENT_CHARACTERISTIC_CONFIG,
397                 BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
398         descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
399         characteristic.addDescriptor(descriptor);
400     }
401 
402     /**
403      * Begins advertising for enrollment
404      *
405      * @param deviceName device name to advertise
406      * @param enrollmentAdvertisingCallback callback for advertiser
407      */
startEnrollmentAdvertising(@ullable String deviceName, AdvertiseCallback enrollmentAdvertisingCallback)408     void startEnrollmentAdvertising(@Nullable String deviceName,
409             AdvertiseCallback enrollmentAdvertisingCallback) {
410         if (enrollmentAdvertisingCallback == null) {
411             if (Log.isLoggable(TAG, Log.DEBUG)) {
412                 Log.d(TAG, "Enrollment Advertising not started: "
413                         + "enrollmentAdvertisingCallback is null");
414             }
415             return;
416         }
417         mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_ENROLLMENT;
418         mEnrollmentAdvertisingCallback = enrollmentAdvertisingCallback;
419         // Replace name to ensure it is small enough to be advertised
420         if (deviceName != null) {
421             BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
422             if (mOriginalBluetoothName == null) {
423                 mOriginalBluetoothName = adapter.getName();
424             }
425             adapter.setName(deviceName);
426             if (Log.isLoggable(TAG, Log.DEBUG)) {
427                 Log.d(TAG, "Changing bluetooth adapter name from "
428                         + mOriginalBluetoothName + " to " + deviceName);
429             }
430         }
431 
432         attemptAdvertising();
433     }
434 
attemptAdvertising()435     private void attemptAdvertising() {
436         // Validate the adapter name change has happened. If not, try again after delay.
437         if (mOriginalBluetoothName != null
438                 && BluetoothAdapter.getDefaultAdapter().getName().equals(mOriginalBluetoothName)) {
439             mHandler.postDelayed(this::attemptAdvertising, BLE_MESSAGE_RETRY_DELAY_MS);
440             if (Log.isLoggable(TAG, Log.DEBUG)) {
441                 Log.d(TAG, "Adapter name change has not taken affect prior to advertising attempt. "
442                         + "Trying again.");
443             }
444             return;
445         }
446 
447         mBlePeripheralManager.startAdvertising(mEnrollmentGattService,
448                 new AdvertiseData.Builder()
449                         .setIncludeDeviceName(true)
450                         .addServiceUuid(new ParcelUuid(mEnrollmentServiceUuid))
451                         .build(),
452                 mEnrollmentAdvertisingCallback);
453     }
454 
stopEnrollmentAdvertising()455     void stopEnrollmentAdvertising() {
456         if (mOriginalBluetoothName != null) {
457             if (Log.isLoggable(TAG, Log.DEBUG)) {
458                 Log.d(TAG, "Changing bluetooth adapter name back to "
459                         + mOriginalBluetoothName);
460             }
461             BluetoothAdapter.getDefaultAdapter().setName(mOriginalBluetoothName);
462             mOriginalBluetoothName = null;
463         }
464         if (mEnrollmentAdvertisingCallback != null) {
465             mBlePeripheralManager.stopAdvertising(mEnrollmentAdvertisingCallback);
466         }
467     }
468 
startUnlockAdvertising()469     void startUnlockAdvertising() {
470         if (mUniqueId == null) {
471             Log.e(TAG, "unique id is null");
472             return;
473         }
474         mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_UNLOCK;
475         mBlePeripheralManager.startAdvertising(mUnlockGattService,
476                 new AdvertiseData.Builder()
477                         .setIncludeDeviceName(false)
478                         .addServiceData(new ParcelUuid(mUnlockServiceUuid), mUniqueId)
479                         .addServiceUuid(new ParcelUuid(mUnlockServiceUuid))
480                         .build(),
481                 mUnlockAdvertisingCallback);
482     }
483 
stopUnlockAdvertising()484     void stopUnlockAdvertising() {
485         mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_NONE;
486         mBlePeripheralManager.stopAdvertising(mUnlockAdvertisingCallback);
487     }
488 
disconnectRemoteDevice()489     void disconnectRemoteDevice() {
490         mBlePeripheralManager.stopGattServer();
491     }
492 
sendMessage(byte[] message, OperationType operation, boolean isPayloadEncrypted, SendMessageCallback callback)493     void sendMessage(byte[] message, OperationType operation, boolean isPayloadEncrypted,
494             SendMessageCallback callback) {
495         if (mMessageStream == null) {
496             Log.e(TAG, "Request to send message, but no valid message stream.");
497             return;
498         }
499 
500         mSendMessageCallback = callback;
501 
502         mMessageStream.writeMessage(message, operation, isPayloadEncrypted);
503     }
504 
505     /**
506      * Sets the given message on the specified characteristic.
507      *
508      * <p>Upon successfully setting of the value, any listeners on the characteristic will be
509      * notified that its value has changed.
510      *
511      * @param device         The device has own the given characteristic.
512      * @param message        The message to set as the characteristic's value.
513      * @param characteristic The characteristic to set the value on.
514      */
setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message, BluetoothGattCharacteristic characteristic)515     private void setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message,
516             BluetoothGattCharacteristic characteristic) {
517         characteristic.setValue(message);
518         mBlePeripheralManager.notifyCharacteristicChanged(device, characteristic, false);
519     }
520 
521     /**
522      * Returns the characteristic that can be written to based on the given UUID.
523      *
524      * <p>The UUID will be one that corresponds to either enrollment or unlock. This method will
525      * return the write characteristic for enrollment or unlock respectively.
526      *
527      * @return The write characteristic or {@code null} if the UUID is invalid.
528      */
529     @Nullable
getCharacteristicForWrite(UUID uuid)530     private BluetoothGattCharacteristic getCharacteristicForWrite(UUID uuid) {
531         if (uuid.equals(mEnrollmentClientWriteUuid)) {
532             return mEnrollmentGattService.getCharacteristic(mEnrollmentServerWriteUuid);
533         }
534 
535         if (uuid.equals(mUnlockClientWriteUuid)) {
536             return mUnlockGattService.getCharacteristic(mUnlockServerWriteUuid);
537         }
538 
539         return null;
540     }
541 
542     private final AdvertiseCallback mUnlockAdvertisingCallback = new AdvertiseCallback() {
543         @Override
544         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
545             super.onStartSuccess(settingsInEffect);
546             if (Log.isLoggable(TAG, Log.DEBUG)) {
547                 Log.d(TAG, "Unlock Advertising onStartSuccess");
548             }
549         }
550 
551         @Override
552         public void onStartFailure(int errorCode) {
553             Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
554             super.onStartFailure(errorCode);
555             if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) {
556                 return;
557             }
558             if (Log.isLoggable(TAG, Log.DEBUG)) {
559                 Log.d(TAG, "Start unlock advertising fail, retry to advertising..");
560             }
561             setupUnlockBleServer();
562             startUnlockAdvertising();
563         }
564     };
565 
566     /**
567      * Adds the given listener to be notified when the characteristic with the given uuid has
568      * received data.
569      */
addDataReceivedListener(UUID uuid, DataReceivedListener listener)570     void addDataReceivedListener(UUID uuid, DataReceivedListener listener) {
571         mDataReceivedListeners.put(uuid, listener);
572         if (Log.isLoggable(TAG, Log.DEBUG)) {
573             Log.d(TAG, "Register DataReceivedListener: " + listener + "for uuid "
574                     + uuid.toString());
575         }
576     }
577 
removeDataReceivedListener(UUID uuid)578     void removeDataReceivedListener(UUID uuid) {
579         if (!mDataReceivedListeners.containsKey(uuid)) {
580             if (Log.isLoggable(TAG, Log.DEBUG)) {
581                 Log.d(TAG, "No DataReceivedListener for uuid " + uuid.toString()
582                         + " to unregister.");
583             }
584             return;
585         }
586         mDataReceivedListeners.remove(uuid);
587         if (Log.isLoggable(TAG, Log.DEBUG)) {
588             Log.d(TAG, "Unregister DataReceivedListener for uuid " + uuid.toString());
589         }
590     }
591 
addBleEventCallback(BleEventCallback callback)592     void addBleEventCallback(BleEventCallback callback) {
593         mBleEventCallbacks.add(callback);
594     }
595 
removeBleEventCallback(BleEventCallback callback)596     void removeBleEventCallback(BleEventCallback callback) {
597         if (!mBleEventCallbacks.remove(callback)) {
598             if (Log.isLoggable(TAG, Log.DEBUG)) {
599                 Log.d(TAG, "Remove BleEventCallback that does not exist.");
600             }
601         }
602     }
603 
604     /**
605      * Callback to be invoked for enrollment events
606      */
607     interface SendMessageCallback {
608 
609         /**
610          * Called when enrollment handshake needs to be terminated
611          */
onSendMessageFailure()612         void onSendMessageFailure();
613     }
614 
615     /**
616      * Callback to be invoked when BLE receives data from remote device.
617      */
618     interface DataReceivedListener {
619 
620         /**
621          * Called when data has been received from a remote device.
622          *
623          * @param value received data
624          */
onDataReceived(byte[] value)625         void onDataReceived(byte[] value);
626     }
627 
628     /**
629      * The interface that device service has to implement to get notified when BLE events occur
630      */
631     interface BleEventCallback {
632 
633         /**
634          * Called when a remote device is connected
635          *
636          * @param device the remote device
637          */
onRemoteDeviceConnected(BluetoothDevice device)638         void onRemoteDeviceConnected(BluetoothDevice device);
639 
640         /**
641          * Called when a remote device is disconnected
642          *
643          * @param device the remote device
644          */
onRemoteDeviceDisconnected(BluetoothDevice device)645         void onRemoteDeviceDisconnected(BluetoothDevice device);
646 
647         /**
648          * Called when the device name of the remote device is retrieved
649          *
650          * @param deviceName device name of the remote device
651          */
onClientDeviceNameRetrieved(String deviceName)652         void onClientDeviceNameRetrieved(String deviceName);
653     }
654 }
655