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.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothGattCharacteristic; 23 import android.bluetooth.BluetoothGattDescriptor; 24 import android.bluetooth.BluetoothGattService; 25 import android.bluetooth.le.AdvertiseCallback; 26 import android.bluetooth.le.AdvertiseData; 27 import android.bluetooth.le.AdvertiseSettings; 28 import android.content.Context; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.ParcelUuid; 32 import android.util.Log; 33 34 import androidx.annotation.Nullable; 35 36 import com.android.car.BLEStreamProtos.BLEMessageProto.BLEMessage; 37 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType; 38 import com.android.car.BLEStreamProtos.VersionExchangeProto.BLEVersionExchange; 39 import com.android.car.CarLocalServices; 40 import com.android.car.R; 41 import com.android.car.Utils; 42 import com.android.car.protobuf.InvalidProtocolBufferException; 43 44 import java.io.IOException; 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.util.LinkedList; 48 import java.util.List; 49 import java.util.Queue; 50 import java.util.UUID; 51 import java.util.concurrent.TimeUnit; 52 53 /** 54 * A BLE Service that is used for communicating with the trusted peer device. This extends from a 55 * more generic {@link BleManager} and has more context on the BLE requirements for the Trusted 56 * device feature. It has knowledge on the GATT services and characteristics that are specific to 57 * the Trusted Device feature. 58 */ 59 class CarTrustAgentBleManager extends BleManager { 60 61 private static final String TAG = "CarTrustBLEManager"; 62 63 /** 64 * The UUID of the Client Characteristic Configuration Descriptor. This descriptor is 65 * responsible for specifying if a characteristic can be subscribed to for notifications. 66 * 67 * @see <a href="https://www.bluetooth.com/specifications/gatt/descriptors/"> 68 * GATT Descriptors</a> 69 */ 70 private static final UUID CLIENT_CHARACTERISTIC_CONFIG = 71 UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); 72 73 /** @hide */ 74 @IntDef(prefix = {"TRUSTED_DEVICE_OPERATION_"}, value = { 75 TRUSTED_DEVICE_OPERATION_NONE, 76 TRUSTED_DEVICE_OPERATION_ENROLLMENT, 77 TRUSTED_DEVICE_OPERATION_UNLOCK 78 }) 79 @Retention(RetentionPolicy.SOURCE) 80 public @interface TrustedDeviceOperation { 81 } 82 83 private static final int TRUSTED_DEVICE_OPERATION_NONE = 0; 84 private static final int TRUSTED_DEVICE_OPERATION_ENROLLMENT = 1; 85 private static final int TRUSTED_DEVICE_OPERATION_UNLOCK = 2; 86 private static final long BLE_MESSAGE_RETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(2); 87 private static final int BLE_MESSAGE_RETRY_LIMIT = 20; 88 89 @TrustedDeviceOperation 90 private int mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_NONE; 91 private CarTrustedDeviceService mCarTrustedDeviceService; 92 private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService; 93 private CarTrustAgentUnlockService mCarTrustAgentUnlockService; 94 private String mOriginalBluetoothName; 95 private byte[] mUniqueId; 96 private String mEnrollmentDeviceName; 97 private int mMtuSize = 20; 98 99 // Enrollment Service and Characteristic UUIDs 100 private UUID mEnrollmentServiceUuid; 101 private UUID mEnrollmentClientWriteUuid; 102 private UUID mEnrollmentServerWriteUuid; 103 private BluetoothGattService mEnrollmentGattService; 104 105 // Unlock Service and Characteristic UUIDs 106 private UUID mUnlockServiceUuid; 107 private UUID mUnlockClientWriteUuid; 108 private UUID mUnlockServerWriteUuid; 109 private BluetoothGattService mUnlockGattService; 110 111 private Queue<BLEMessage> mMessageQueue = new LinkedList<>(); 112 private BLEMessagePayloadStream mBleMessagePayloadStream = new BLEMessagePayloadStream(); 113 114 // This is a boolean because there's only one supported version. 115 private boolean mIsVersionExchanged; 116 private int mBleMessageRetryStartCount; 117 private Handler mHandler = new Handler(Looper.getMainLooper()); 118 private Runnable mSendRepeatedBleMessage; 119 CarTrustAgentBleManager(Context context)120 CarTrustAgentBleManager(Context context) { 121 super(context); 122 } 123 124 // Overriding some of the {@link BLEManager} methods to be specific for Trusted Device feature. 125 @Override onRemoteDeviceConnected(BluetoothDevice device)126 public void onRemoteDeviceConnected(BluetoothDevice device) { 127 if (getTrustedDeviceService() == null) { 128 return; 129 } 130 131 // Retrieving device name only happens in enrollment, the retrieved device name will be 132 // stored in sharedPreference for further use. 133 if (mCurrentTrustedDeviceOperation == TRUSTED_DEVICE_OPERATION_ENROLLMENT 134 && device.getName() == null) { 135 retrieveDeviceName(device); 136 } 137 138 mMessageQueue.clear(); 139 mIsVersionExchanged = false; 140 getTrustedDeviceService().onRemoteDeviceConnected(device); 141 if (mSendRepeatedBleMessage != null) { 142 mHandler.removeCallbacks(mSendRepeatedBleMessage); 143 mSendRepeatedBleMessage = null; 144 } 145 } 146 147 @Override onRemoteDeviceDisconnected(BluetoothDevice device)148 public void onRemoteDeviceDisconnected(BluetoothDevice device) { 149 if (getTrustedDeviceService() != null) { 150 getTrustedDeviceService().onRemoteDeviceDisconnected(device); 151 } 152 153 mMessageQueue.clear(); 154 mIsVersionExchanged = false; 155 mBleMessagePayloadStream.reset(); 156 157 if (mSendRepeatedBleMessage != null) { 158 mHandler.removeCallbacks(mSendRepeatedBleMessage); 159 } 160 mSendRepeatedBleMessage = null; 161 } 162 163 @Override onDeviceNameRetrieved(@ullable String deviceName)164 protected void onDeviceNameRetrieved(@Nullable String deviceName) { 165 if (getTrustedDeviceService() != null) { 166 getTrustedDeviceService().onDeviceNameRetrieved(deviceName); 167 } 168 } 169 170 @Override onMtuSizeChanged(int size)171 protected void onMtuSizeChanged(int size) { 172 mMtuSize = size; 173 } 174 175 @Override onCharacteristicWrite(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)176 public void onCharacteristicWrite(BluetoothDevice device, int requestId, 177 BluetoothGattCharacteristic characteristic, boolean preparedWrite, 178 boolean responseNeeded, int offset, byte[] value) { 179 UUID uuid = characteristic.getUuid(); 180 if (Log.isLoggable(TAG, Log.DEBUG)) { 181 Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid); 182 } 183 184 if (!mIsVersionExchanged) { 185 resolveBLEVersion(device, value, uuid); 186 return; 187 } 188 189 BLEMessage message; 190 try { 191 message = BLEMessage.parseFrom(value); 192 } catch (InvalidProtocolBufferException e) { 193 Log.e(TAG, "Can not parse BLE message", e); 194 return; 195 } 196 197 if (message.getOperation() == OperationType.ACK) { 198 handleClientAckMessage(device, uuid); 199 return; 200 } 201 202 // This write operation is not thread safe individually, but is guarded by the callback 203 // here. 204 try { 205 mBleMessagePayloadStream.write(message); 206 } catch (IOException e) { 207 Log.e(TAG, "Can write the BLE message's payload", e); 208 return; 209 } 210 211 if (!mBleMessagePayloadStream.isComplete()) { 212 // If it's not complete, make sure the client knows that this message was received. 213 sendAcknowledgmentMessage(device, uuid); 214 return; 215 } 216 217 if (uuid.equals(mEnrollmentClientWriteUuid)) { 218 if (getEnrollmentService() != null) { 219 getEnrollmentService().onEnrollmentDataReceived( 220 mBleMessagePayloadStream.toByteArray()); 221 } 222 } else if (uuid.equals(mUnlockClientWriteUuid)) { 223 if (getUnlockService() != null) { 224 getUnlockService().onUnlockDataReceived(mBleMessagePayloadStream.toByteArray()); 225 } 226 } 227 228 mBleMessagePayloadStream.reset(); 229 } 230 231 @Override onCharacteristicRead(BluetoothDevice device, int requestId, int offset, final BluetoothGattCharacteristic characteristic)232 public void onCharacteristicRead(BluetoothDevice device, int requestId, int offset, 233 final BluetoothGattCharacteristic characteristic) { 234 // Ignored read requests. 235 } 236 237 @Nullable getTrustedDeviceService()238 private CarTrustedDeviceService getTrustedDeviceService() { 239 if (mCarTrustedDeviceService == null) { 240 mCarTrustedDeviceService = CarLocalServices.getService(CarTrustedDeviceService.class); 241 } 242 return mCarTrustedDeviceService; 243 } 244 245 @Nullable getEnrollmentService()246 private CarTrustAgentEnrollmentService getEnrollmentService() { 247 if (mCarTrustAgentEnrollmentService != null) { 248 return mCarTrustAgentEnrollmentService; 249 } 250 251 if (getTrustedDeviceService() != null) { 252 mCarTrustAgentEnrollmentService = 253 getTrustedDeviceService().getCarTrustAgentEnrollmentService(); 254 } 255 return mCarTrustAgentEnrollmentService; 256 } 257 258 @Nullable getUnlockService()259 private CarTrustAgentUnlockService getUnlockService() { 260 if (mCarTrustAgentUnlockService != null) { 261 return mCarTrustAgentUnlockService; 262 } 263 264 if (getTrustedDeviceService() != null) { 265 mCarTrustAgentUnlockService = getTrustedDeviceService().getCarTrustAgentUnlockService(); 266 } 267 return mCarTrustAgentUnlockService; 268 } 269 270 @Nullable getUniqueId()271 private byte[] getUniqueId() { 272 if (mUniqueId != null) { 273 return mUniqueId; 274 } 275 276 if (getTrustedDeviceService() != null && getTrustedDeviceService().getUniqueId() != null) { 277 mUniqueId = Utils.uuidToBytes(getTrustedDeviceService().getUniqueId()); 278 } 279 return mUniqueId; 280 } 281 282 @Nullable getEnrollmentDeviceName()283 private String getEnrollmentDeviceName() { 284 if (mEnrollmentDeviceName != null) { 285 return mEnrollmentDeviceName; 286 } 287 288 if (getTrustedDeviceService() != null) { 289 mEnrollmentDeviceName = getTrustedDeviceService().getEnrollmentDeviceName(); 290 } 291 return mEnrollmentDeviceName; 292 } 293 resolveBLEVersion(BluetoothDevice device, byte[] value, UUID clientCharacteristicUUID)294 private void resolveBLEVersion(BluetoothDevice device, byte[] value, 295 UUID clientCharacteristicUUID) { 296 BluetoothGattCharacteristic characteristic = 297 getCharacteristicForWrite(clientCharacteristicUUID); 298 299 if (characteristic == null) { 300 Log.e(TAG, "Invalid UUID (" + clientCharacteristicUUID 301 + ") during version exchange; disconnecting from remote device."); 302 disconnectRemoteDevice(); 303 return; 304 } 305 306 BLEVersionExchange deviceVersion; 307 try { 308 deviceVersion = BLEVersionExchange.parseFrom(value); 309 } catch (InvalidProtocolBufferException e) { 310 disconnectRemoteDevice(); 311 Log.e(TAG, "Could not parse version exchange message", e); 312 return; 313 } 314 315 if (!BLEVersionExchangeResolver.hasSupportedVersion(deviceVersion)) { 316 Log.e(TAG, "No supported version found during version exchange."); 317 disconnectRemoteDevice(); 318 return; 319 } 320 321 BLEVersionExchange headunitVersion = BLEVersionExchangeResolver.makeVersionExchange(); 322 setValueOnCharacteristicAndNotify(device, headunitVersion.toByteArray(), characteristic); 323 324 if (Log.isLoggable(TAG, Log.DEBUG)) { 325 Log.d(TAG, "Sent supported version to the phone."); 326 } 327 328 mIsVersionExchanged = true; 329 } 330 331 /** 332 * Setup the BLE GATT server for Enrollment. The GATT server for Enrollment comprises of one 333 * GATT Service and 2 characteristics - one for the phone to write to and one for the head unit 334 * to write to. 335 */ setupEnrollmentBleServer()336 void setupEnrollmentBleServer() { 337 mEnrollmentServiceUuid = UUID.fromString( 338 getContext().getString(R.string.enrollment_service_uuid)); 339 mEnrollmentClientWriteUuid = UUID.fromString( 340 getContext().getString(R.string.enrollment_client_write_uuid)); 341 mEnrollmentServerWriteUuid = UUID.fromString( 342 getContext().getString(R.string.enrollment_server_write_uuid)); 343 344 mEnrollmentGattService = new BluetoothGattService(mEnrollmentServiceUuid, 345 BluetoothGattService.SERVICE_TYPE_PRIMARY); 346 347 // Characteristic the connected bluetooth device will write to. 348 BluetoothGattCharacteristic clientCharacteristic = 349 new BluetoothGattCharacteristic(mEnrollmentClientWriteUuid, 350 BluetoothGattCharacteristic.PROPERTY_WRITE, 351 BluetoothGattCharacteristic.PERMISSION_WRITE); 352 353 // Characteristic that this manager will write to. 354 BluetoothGattCharacteristic serverCharacteristic = 355 new BluetoothGattCharacteristic(mEnrollmentServerWriteUuid, 356 BluetoothGattCharacteristic.PROPERTY_NOTIFY, 357 BluetoothGattCharacteristic.PERMISSION_READ); 358 359 addDescriptorToCharacteristic(serverCharacteristic); 360 361 mEnrollmentGattService.addCharacteristic(clientCharacteristic); 362 mEnrollmentGattService.addCharacteristic(serverCharacteristic); 363 } 364 365 /** 366 * Setup the BLE GATT server for Unlocking the Head unit. The GATT server for this phase also 367 * comprises of 1 Service and 2 characteristics. However both the token and the handle are sent 368 * from the phone to the head unit. 369 */ setupUnlockBleServer()370 void setupUnlockBleServer() { 371 mUnlockServiceUuid = UUID.fromString(getContext().getString(R.string.unlock_service_uuid)); 372 mUnlockClientWriteUuid = UUID 373 .fromString(getContext().getString(R.string.unlock_client_write_uuid)); 374 mUnlockServerWriteUuid = UUID 375 .fromString(getContext().getString(R.string.unlock_server_write_uuid)); 376 377 mUnlockGattService = new BluetoothGattService(mUnlockServiceUuid, 378 BluetoothGattService.SERVICE_TYPE_PRIMARY); 379 380 // Characteristic the connected bluetooth device will write to. 381 BluetoothGattCharacteristic clientCharacteristic = new BluetoothGattCharacteristic( 382 mUnlockClientWriteUuid, 383 BluetoothGattCharacteristic.PROPERTY_WRITE, 384 BluetoothGattCharacteristic.PERMISSION_WRITE); 385 386 // Characteristic that this manager will write to. 387 BluetoothGattCharacteristic serverCharacteristic = new BluetoothGattCharacteristic( 388 mUnlockServerWriteUuid, 389 BluetoothGattCharacteristic.PROPERTY_NOTIFY, 390 BluetoothGattCharacteristic.PERMISSION_READ); 391 392 addDescriptorToCharacteristic(serverCharacteristic); 393 394 mUnlockGattService.addCharacteristic(clientCharacteristic); 395 mUnlockGattService.addCharacteristic(serverCharacteristic); 396 } 397 addDescriptorToCharacteristic(BluetoothGattCharacteristic characteristic)398 private void addDescriptorToCharacteristic(BluetoothGattCharacteristic characteristic) { 399 BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor( 400 CLIENT_CHARACTERISTIC_CONFIG, 401 BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE); 402 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 403 characteristic.addDescriptor(descriptor); 404 } 405 startEnrollmentAdvertising()406 void startEnrollmentAdvertising() { 407 mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_ENROLLMENT; 408 // Replace name to ensure it is small enough to be advertised 409 String name = getEnrollmentDeviceName(); 410 if (name != null) { 411 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 412 if (mOriginalBluetoothName == null) { 413 mOriginalBluetoothName = adapter.getName(); 414 } 415 adapter.setName(name); 416 if (Log.isLoggable(TAG, Log.DEBUG)) { 417 Log.d(TAG, "Changing bluetooth adapter name from " 418 + mOriginalBluetoothName + " to " + name); 419 } 420 } 421 startAdvertising(mEnrollmentGattService, 422 new AdvertiseData.Builder() 423 .setIncludeDeviceName(true) 424 .addServiceUuid(new ParcelUuid(mEnrollmentServiceUuid)) 425 .build(), 426 mEnrollmentAdvertisingCallback); 427 } 428 stopEnrollmentAdvertising()429 void stopEnrollmentAdvertising() { 430 if (mOriginalBluetoothName != null) { 431 if (Log.isLoggable(TAG, Log.DEBUG)) { 432 Log.d(TAG, "Changing bluetooth adapter name back to " 433 + mOriginalBluetoothName); 434 } 435 BluetoothAdapter.getDefaultAdapter().setName(mOriginalBluetoothName); 436 } 437 stopAdvertising(mEnrollmentAdvertisingCallback); 438 } 439 startUnlockAdvertising()440 void startUnlockAdvertising() { 441 mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_UNLOCK; 442 startAdvertising(mUnlockGattService, 443 new AdvertiseData.Builder() 444 .setIncludeDeviceName(false) 445 .addServiceData(new ParcelUuid(mUnlockServiceUuid), getUniqueId()) 446 .addServiceUuid(new ParcelUuid(mUnlockServiceUuid)) 447 .build(), 448 mUnlockAdvertisingCallback); 449 } 450 stopUnlockAdvertising()451 void stopUnlockAdvertising() { 452 mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_NONE; 453 stopAdvertising(mUnlockAdvertisingCallback); 454 } 455 disconnectRemoteDevice()456 void disconnectRemoteDevice() { 457 stopGattServer(); 458 } 459 sendUnlockMessage(BluetoothDevice device, byte[] message, OperationType operation, boolean isPayloadEncrypted)460 void sendUnlockMessage(BluetoothDevice device, byte[] message, OperationType operation, 461 boolean isPayloadEncrypted) { 462 BluetoothGattCharacteristic writeCharacteristic = mUnlockGattService 463 .getCharacteristic(mUnlockServerWriteUuid); 464 465 sendMessage(device, writeCharacteristic, message, operation, isPayloadEncrypted); 466 } 467 sendEnrollmentMessage(BluetoothDevice device, byte[] message, OperationType operation, boolean isPayloadEncrypted)468 void sendEnrollmentMessage(BluetoothDevice device, byte[] message, OperationType operation, 469 boolean isPayloadEncrypted) { 470 BluetoothGattCharacteristic writeCharacteristic = mEnrollmentGattService 471 .getCharacteristic(mEnrollmentServerWriteUuid); 472 473 sendMessage(device, writeCharacteristic, message, operation, isPayloadEncrypted); 474 } 475 476 /** 477 * Handles an ACK from the client. 478 * 479 * <p>An ACK means that the client has successfully received a partial BLEMessage, meaning the 480 * next part of the message can be sent. 481 * 482 * @param device The client device. 483 * @param clientCharacteristicUUID The UUID of the characteristic on the device that the ACK 484 * was written to. 485 */ handleClientAckMessage(BluetoothDevice device, UUID clientCharacteristicUUID)486 private void handleClientAckMessage(BluetoothDevice device, UUID clientCharacteristicUUID) { 487 if (Log.isLoggable(TAG, Log.DEBUG)) { 488 Log.d(TAG, "Received ACK from client. Attempting to write next message in queue. " 489 + "UUID: " + clientCharacteristicUUID); 490 } 491 492 BluetoothGattCharacteristic writeCharacteristic = 493 getCharacteristicForWrite(clientCharacteristicUUID); 494 495 if (writeCharacteristic == null) { 496 Log.e(TAG, "No corresponding write characteristic found for writing next message in" 497 + " queue. UUID: " + clientCharacteristicUUID); 498 return; 499 } 500 if (mSendRepeatedBleMessage != null) { 501 mHandler.removeCallbacks(mSendRepeatedBleMessage); 502 mSendRepeatedBleMessage = null; 503 } 504 // Previous message has been sent successfully so we can start the next message. 505 mMessageQueue.remove(); 506 writeNextMessageInQueue(device, writeCharacteristic); 507 } 508 509 /** 510 * Sends the given message to the specified device and characteristic. 511 * The message will be splited into multiple messages wrapped in BLEMessage proto. 512 * 513 * @param device The device to send the message to. 514 * @param characteristic The characteristic to write to. 515 * @param message A message to send. 516 * @param operation The type of operation this message represents. 517 * @param isPayloadEncrypted {@code true} if the message is encrypted. 518 */ sendMessage(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] message, OperationType operation, boolean isPayloadEncrypted)519 private void sendMessage(BluetoothDevice device, BluetoothGattCharacteristic characteristic, 520 byte[] message, OperationType operation, boolean isPayloadEncrypted) { 521 if (Log.isLoggable(TAG, Log.DEBUG)) { 522 Log.d(TAG, "sendMessage to: " + device.getAddress() + "; and characteristic UUID: " 523 + characteristic.getUuid()); 524 } 525 526 List<BLEMessage> bleMessages = BLEMessageV1Factory.makeBLEMessages(message, operation, 527 mMtuSize, isPayloadEncrypted); 528 529 if (Log.isLoggable(TAG, Log.DEBUG)) { 530 Log.d(TAG, "sending " + bleMessages.size() + " messages to device"); 531 } 532 533 mMessageQueue.addAll(bleMessages); 534 writeNextMessageInQueue(device, characteristic); 535 } 536 537 /** 538 * Writes the next message in {@link #mMessageQueue} to the given characteristic. 539 * 540 * <p>If the message queue is empty, then this method will do nothing. 541 */ writeNextMessageInQueue(BluetoothDevice device, BluetoothGattCharacteristic characteristic)542 private void writeNextMessageInQueue(BluetoothDevice device, 543 BluetoothGattCharacteristic characteristic) { 544 if (mMessageQueue.isEmpty()) { 545 Log.e(TAG, "Call to write next message in queue, but the message queue is empty"); 546 return; 547 } 548 // When there is only one message, no ACKs are sent, so we no need to retry based on ACKs. 549 if (mMessageQueue.size() == 1) { 550 setValueOnCharacteristicAndNotify(device, mMessageQueue.remove().toByteArray(), 551 characteristic); 552 return; 553 } 554 mBleMessageRetryStartCount = 0; 555 mSendRepeatedBleMessage = new Runnable() { 556 @Override 557 public void run() { 558 if (Log.isLoggable(TAG, Log.DEBUG)) { 559 Log.d(TAG, "BLE message sending... " + "retry count: " 560 + mBleMessageRetryStartCount); 561 } 562 if (mBleMessageRetryStartCount < BLE_MESSAGE_RETRY_LIMIT) { 563 setValueOnCharacteristicAndNotify(device, mMessageQueue.peek().toByteArray(), 564 characteristic); 565 mBleMessageRetryStartCount++; 566 mHandler.postDelayed(this, BLE_MESSAGE_RETRY_DELAY_MS); 567 } else { 568 Log.e(TAG, "Error during BLE message sending - exceeded retry limit."); 569 mHandler.removeCallbacks(this); 570 mCarTrustAgentEnrollmentService.terminateEnrollmentHandshake(); 571 mSendRepeatedBleMessage = null; 572 } 573 } 574 }; 575 mHandler.post(mSendRepeatedBleMessage); 576 } 577 sendAcknowledgmentMessage(BluetoothDevice device, UUID clientCharacteristicUUID)578 private void sendAcknowledgmentMessage(BluetoothDevice device, UUID clientCharacteristicUUID) { 579 BluetoothGattCharacteristic writeCharacteristic = 580 getCharacteristicForWrite(clientCharacteristicUUID); 581 582 if (writeCharacteristic == null) { 583 Log.e(TAG, "No corresponding write characteristic found for sending ACK. UUID: " 584 + clientCharacteristicUUID); 585 return; 586 } 587 588 setValueOnCharacteristicAndNotify(device, 589 BLEMessageV1Factory.makeAcknowledgementMessage().toByteArray(), 590 writeCharacteristic); 591 } 592 593 /** 594 * Sets the given message on the specified characteristic. 595 * 596 * <p>Upon successfully setting of the value, any listeners on the characteristic will be 597 * notified that its value has changed. 598 * 599 * @param device The device has own the given characteristic. 600 * @param message The message to set as the characteristic's value. 601 * @param characteristic The characteristic to set the value on. 602 */ setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message, BluetoothGattCharacteristic characteristic)603 private void setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message, 604 BluetoothGattCharacteristic characteristic) { 605 characteristic.setValue(message); 606 notifyCharacteristicChanged(device, characteristic, false); 607 } 608 609 /** 610 * Returns the characteristic that can be written to based on the given UUID. 611 * 612 * <p>The UUID will be one that corresponds to either enrollment or unlock. This method will 613 * return the write characteristic for enrollment or unlock respectively. 614 * 615 * @return The write characteristic or {@code null} if the UUID is invalid. 616 */ 617 @Nullable getCharacteristicForWrite(UUID uuid)618 private BluetoothGattCharacteristic getCharacteristicForWrite(UUID uuid) { 619 if (uuid.equals(mEnrollmentClientWriteUuid)) { 620 return mEnrollmentGattService.getCharacteristic(mEnrollmentServerWriteUuid); 621 } 622 623 if (uuid.equals(mUnlockClientWriteUuid)) { 624 return mUnlockGattService.getCharacteristic(mUnlockServerWriteUuid); 625 } 626 627 return null; 628 } 629 630 private final AdvertiseCallback mEnrollmentAdvertisingCallback = new AdvertiseCallback() { 631 @Override 632 public void onStartSuccess(AdvertiseSettings settingsInEffect) { 633 super.onStartSuccess(settingsInEffect); 634 if (getEnrollmentService() != null) { 635 getEnrollmentService().onEnrollmentAdvertiseStartSuccess(); 636 } 637 if (Log.isLoggable(TAG, Log.DEBUG)) { 638 Log.d(TAG, "Successfully started advertising service"); 639 } 640 } 641 642 @Override 643 public void onStartFailure(int errorCode) { 644 Log.e(TAG, "Failed to advertise, errorCode: " + errorCode); 645 646 super.onStartFailure(errorCode); 647 if (getEnrollmentService() != null) { 648 getEnrollmentService().onEnrollmentAdvertiseStartFailure(); 649 } 650 } 651 }; 652 653 private final AdvertiseCallback mUnlockAdvertisingCallback = new AdvertiseCallback() { 654 @Override 655 public void onStartSuccess(AdvertiseSettings settingsInEffect) { 656 super.onStartSuccess(settingsInEffect); 657 if (Log.isLoggable(TAG, Log.DEBUG)) { 658 Log.d(TAG, "Unlock Advertising onStartSuccess"); 659 } 660 } 661 662 @Override 663 public void onStartFailure(int errorCode) { 664 Log.e(TAG, "Failed to advertise, errorCode: " + errorCode); 665 super.onStartFailure(errorCode); 666 if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) { 667 return; 668 } 669 if (Log.isLoggable(TAG, Log.DEBUG)) { 670 Log.d(TAG, "Start unlock advertising fail, retry to advertising.."); 671 } 672 setupUnlockBleServer(); 673 startUnlockAdvertising(); 674 } 675 }; 676 } 677