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