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