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 static com.android.car.trust.EventLog.CLIENT_AUTHENTICATED; 20 import static com.android.car.trust.EventLog.RECEIVED_DEVICE_ID; 21 import static com.android.car.trust.EventLog.REMOTE_DEVICE_CONNECTED; 22 import static com.android.car.trust.EventLog.START_UNLOCK_ADVERTISING; 23 import static com.android.car.trust.EventLog.STOP_UNLOCK_ADVERTISING; 24 import static com.android.car.trust.EventLog.UNLOCK_CREDENTIALS_RECEIVED; 25 import static com.android.car.trust.EventLog.UNLOCK_ENCRYPTION_STATE; 26 import static com.android.car.trust.EventLog.UNLOCK_SERVICE_INIT; 27 import static com.android.car.trust.EventLog.WAITING_FOR_CLIENT_AUTH; 28 import static com.android.car.trust.EventLog.logUnlockEvent; 29 30 import android.annotation.IntDef; 31 import android.annotation.Nullable; 32 import android.bluetooth.BluetoothDevice; 33 import android.car.encryptionrunner.EncryptionRunner; 34 import android.car.encryptionrunner.EncryptionRunnerFactory; 35 import android.car.encryptionrunner.HandshakeException; 36 import android.car.encryptionrunner.HandshakeMessage; 37 import android.car.encryptionrunner.Key; 38 import android.content.SharedPreferences; 39 import android.util.Log; 40 41 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType; 42 import com.android.car.PhoneAuthProtos.PhoneAuthProto.PhoneCredentials; 43 import com.android.car.Utils; 44 import com.android.car.protobuf.InvalidProtocolBufferException; 45 import com.android.internal.annotations.GuardedBy; 46 47 import com.google.security.cryptauth.lib.securegcm.D2DConnectionContext; 48 import com.google.security.cryptauth.lib.securemessage.CryptoOps; 49 50 import java.io.PrintWriter; 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.security.InvalidKeyException; 54 import java.security.MessageDigest; 55 import java.security.NoSuchAlgorithmException; 56 import java.security.SignatureException; 57 import java.util.LinkedList; 58 import java.util.Queue; 59 import java.util.UUID; 60 61 import javax.crypto.spec.SecretKeySpec; 62 63 /** 64 * A service that interacts with the Trust Agent {@link CarBleTrustAgent} and a comms (BLE) service 65 * {@link CarTrustAgentBleManager} to receive the necessary credentials to authenticate 66 * an Android user. 67 * 68 * <p> 69 * The unlock flow is as follows: 70 * <ol> 71 * <li>IHU advertises via BLE when it is in a locked state. The advertisement includes its 72 * identifier. 73 * <li>Phone (Trusted device) scans, finds and connects to the IHU. 74 * <li>Protocol versions are exchanged and verified. 75 * <li>Phone sends its identifier in plain text. 76 * <li>IHU verifies that the phone is enrolled as a trusted device from its identifier. 77 * <li>IHU, then sends an ACK back to the phone. 78 * <li>Phone & IHU go over the key exchange (using UKEY2) for encrypting this new session. 79 * <li>Key exchange is completed without any numeric comparison. 80 * <li>Phone sends its MAC (digest) that is computed from the context from this new session and the 81 * previous session. 82 * <li>IHU computes Phone's MAC and validates against what the phone sent. On validation failure, 83 * the stored encryption keys for the phone are deleted. This would require the phone to re-enroll 84 * again. 85 * <li>IHU sends its MAC that is computed similarly from the new session and previous session 86 * contexts. 87 * <li>Phone computes IHU's MAC internally and validates it against what it received. 88 * <li>At this point, the devices have mutually authenticated each other and also have keys to 89 * encrypt 90 * current session. 91 * <li>IHU saves the current session keys. This would serve for authenticating the next session. 92 * <li>Phone sends the encrypted escrow token and handle to the IHU. 93 * <li>IHU retrieves the user id and authenticates the user. 94 * </ol> 95 */ 96 public class CarTrustAgentUnlockService { 97 private static final String TAG = "CarTrustAgentUnlock"; 98 private static final String TRUSTED_DEVICE_UNLOCK_ENABLED_KEY = "trusted_device_unlock_enabled"; 99 100 // Arbitrary log size 101 private static final int MAX_LOG_SIZE = 20; 102 private static final byte[] RESUME = "RESUME".getBytes(); 103 private static final byte[] SERVER = "SERVER".getBytes(); 104 private static final byte[] CLIENT = "CLIENT".getBytes(); 105 private static final int RESUME_HMAC_LENGTH = 32; 106 107 private static final byte[] ACKNOWLEDGEMENT_MESSAGE = "ACK".getBytes(); 108 109 // State of the unlock process. Important to maintain the same order in both phone and IHU. 110 // State increments to the next state on successful completion. 111 private static final int UNLOCK_STATE_WAITING_FOR_UNIQUE_ID = 0; 112 private static final int UNLOCK_STATE_KEY_EXCHANGE_IN_PROGRESS = 1; 113 private static final int UNLOCK_STATE_WAITING_FOR_CLIENT_AUTH = 2; 114 private static final int UNLOCK_STATE_MUTUAL_AUTH_ESTABLISHED = 3; 115 private static final int UNLOCK_STATE_PHONE_CREDENTIALS_RECEIVED = 4; 116 117 /** @hide */ 118 @Retention(RetentionPolicy.SOURCE) 119 @IntDef(prefix = {"UNLOCK_STATE_"}, value = {UNLOCK_STATE_WAITING_FOR_UNIQUE_ID, 120 UNLOCK_STATE_KEY_EXCHANGE_IN_PROGRESS, UNLOCK_STATE_WAITING_FOR_CLIENT_AUTH, 121 UNLOCK_STATE_MUTUAL_AUTH_ESTABLISHED, UNLOCK_STATE_PHONE_CREDENTIALS_RECEIVED}) 122 @interface UnlockState { 123 } 124 125 @UnlockState 126 private int mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_UNIQUE_ID; 127 128 private final CarTrustedDeviceService mTrustedDeviceService; 129 private final CarTrustAgentBleManager mCarTrustAgentBleManager; 130 private CarTrustAgentUnlockDelegate mUnlockDelegate; 131 private String mClientDeviceId; 132 private final Queue<String> mLogQueue = new LinkedList<>(); 133 134 // Locks 135 private final Object mDeviceLock = new Object(); 136 137 @GuardedBy("mDeviceLock") 138 private BluetoothDevice mRemoteUnlockDevice; 139 140 private EncryptionRunner mEncryptionRunner = EncryptionRunnerFactory.newRunner(); 141 private HandshakeMessage mHandshakeMessage; 142 private Key mEncryptionKey; 143 @HandshakeMessage.HandshakeState 144 private int mEncryptionState = HandshakeMessage.HandshakeState.UNKNOWN; 145 146 private D2DConnectionContext mPrevContext; 147 private D2DConnectionContext mCurrentContext; 148 CarTrustAgentUnlockService(CarTrustedDeviceService service, CarTrustAgentBleManager bleService)149 CarTrustAgentUnlockService(CarTrustedDeviceService service, 150 CarTrustAgentBleManager bleService) { 151 mTrustedDeviceService = service; 152 mCarTrustAgentBleManager = bleService; 153 } 154 155 /** 156 * The interface that an unlock delegate has to implement to get the auth credentials from 157 * the unlock service. 158 */ 159 interface CarTrustAgentUnlockDelegate { 160 /** 161 * Called when the Unlock service has the auth credentials to pass. 162 * 163 * @param user user being authorized 164 * @param token escrow token for the user 165 * @param handle the handle corresponding to the escrow token 166 */ onUnlockDataReceived(int user, byte[] token, long handle)167 void onUnlockDataReceived(int user, byte[] token, long handle); 168 } 169 170 /** 171 * Enable or disable authentication of the head unit with a trusted device. 172 * 173 * @param isEnabled when set to {@code false}, head unit will not be 174 * discoverable to unlock the user. Setting it to {@code true} will enable it 175 * back. 176 */ setTrustedDeviceUnlockEnabled(boolean isEnabled)177 public void setTrustedDeviceUnlockEnabled(boolean isEnabled) { 178 SharedPreferences.Editor editor = mTrustedDeviceService.getSharedPrefs().edit(); 179 editor.putBoolean(TRUSTED_DEVICE_UNLOCK_ENABLED_KEY, isEnabled); 180 if (!editor.commit()) { 181 Log.wtf(TAG, "Unlock Enable Failed. Enable? " + isEnabled); 182 } 183 } 184 185 /** 186 * Set a delegate that implements {@link CarTrustAgentUnlockDelegate}. The delegate will be 187 * handed the auth related data (token and handle) when it is received from the remote 188 * trusted device. The delegate is expected to use that to authorize the user. 189 */ setUnlockRequestDelegate(CarTrustAgentUnlockDelegate delegate)190 void setUnlockRequestDelegate(CarTrustAgentUnlockDelegate delegate) { 191 mUnlockDelegate = delegate; 192 } 193 194 /** 195 * Start Unlock Advertising 196 */ startUnlockAdvertising()197 void startUnlockAdvertising() { 198 if (!mTrustedDeviceService.getSharedPrefs().getBoolean(TRUSTED_DEVICE_UNLOCK_ENABLED_KEY, 199 true)) { 200 Log.e(TAG, "Trusted Device Unlock is disabled"); 201 return; 202 } 203 mTrustedDeviceService.getCarTrustAgentEnrollmentService().stopEnrollmentAdvertising(); 204 stopUnlockAdvertising(); 205 206 logUnlockEvent(START_UNLOCK_ADVERTISING); 207 queueMessageForLog("startUnlockAdvertising"); 208 mCarTrustAgentBleManager.startUnlockAdvertising(); 209 } 210 211 /** 212 * Stop unlock advertising 213 */ stopUnlockAdvertising()214 void stopUnlockAdvertising() { 215 logUnlockEvent(STOP_UNLOCK_ADVERTISING); 216 queueMessageForLog("stopUnlockAdvertising"); 217 mCarTrustAgentBleManager.stopUnlockAdvertising(); 218 // Also disconnect from the peer. 219 if (mRemoteUnlockDevice != null) { 220 mCarTrustAgentBleManager.disconnectRemoteDevice(); 221 mRemoteUnlockDevice = null; 222 } 223 } 224 init()225 void init() { 226 logUnlockEvent(UNLOCK_SERVICE_INIT); 227 mCarTrustAgentBleManager.setupUnlockBleServer(); 228 } 229 release()230 void release() { 231 synchronized (mDeviceLock) { 232 mRemoteUnlockDevice = null; 233 } 234 mPrevContext = null; 235 mCurrentContext = null; 236 } 237 onRemoteDeviceConnected(BluetoothDevice device)238 void onRemoteDeviceConnected(BluetoothDevice device) { 239 synchronized (mDeviceLock) { 240 if (mRemoteUnlockDevice != null) { 241 // TBD, return when this is encountered? 242 Log.e(TAG, "Unexpected: Cannot connect to another device when already connected"); 243 } 244 queueMessageForLog("onRemoteDeviceConnected (addr:" + device.getAddress() + ")"); 245 logUnlockEvent(REMOTE_DEVICE_CONNECTED); 246 mRemoteUnlockDevice = device; 247 } 248 resetEncryptionState(); 249 mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_UNIQUE_ID; 250 } 251 onRemoteDeviceDisconnected(BluetoothDevice device)252 void onRemoteDeviceDisconnected(BluetoothDevice device) { 253 // sanity checking 254 if (!device.equals(mRemoteUnlockDevice) && device.getAddress() != null) { 255 Log.e(TAG, "Disconnected from an unknown device:" + device.getAddress()); 256 } 257 queueMessageForLog("onRemoteDeviceDisconnected (addr:" + device.getAddress() + ")"); 258 synchronized (mDeviceLock) { 259 mRemoteUnlockDevice = null; 260 } 261 resetEncryptionState(); 262 mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_UNIQUE_ID; 263 } 264 onUnlockDataReceived(byte[] value)265 void onUnlockDataReceived(byte[] value) { 266 switch (mCurrentUnlockState) { 267 case UNLOCK_STATE_WAITING_FOR_UNIQUE_ID: 268 if (!CarTrustAgentValidator.isValidUnlockDeviceId(value)) { 269 Log.e(TAG, "Device Id rejected by validator."); 270 resetUnlockStateOnFailure(); 271 return; 272 } 273 mClientDeviceId = convertToDeviceId(value); 274 if (mClientDeviceId == null) { 275 if (Log.isLoggable(TAG, Log.DEBUG)) { 276 Log.d(TAG, "Phone not enrolled as a trusted device"); 277 } 278 resetUnlockStateOnFailure(); 279 return; 280 } 281 logUnlockEvent(RECEIVED_DEVICE_ID); 282 sendAckToClient(/* isEncrypted = */ false); 283 // Next step is to wait for the client to start the encryption handshake. 284 mCurrentUnlockState = UNLOCK_STATE_KEY_EXCHANGE_IN_PROGRESS; 285 break; 286 case UNLOCK_STATE_KEY_EXCHANGE_IN_PROGRESS: 287 try { 288 processKeyExchangeHandshakeMessage(value); 289 } catch (HandshakeException e) { 290 Log.e(TAG, "Handshake failure", e); 291 resetUnlockStateOnFailure(); 292 } 293 break; 294 case UNLOCK_STATE_WAITING_FOR_CLIENT_AUTH: 295 if (!authenticateClient(value)) { 296 if (Log.isLoggable(TAG, Log.DEBUG)) { 297 Log.d(TAG, "HMAC from the phone is not correct. Cannot resume session. Need" 298 + " to re-enroll"); 299 } 300 mTrustedDeviceService.clearEncryptionKey(mClientDeviceId); 301 resetUnlockStateOnFailure(); 302 return; 303 } 304 305 logUnlockEvent(CLIENT_AUTHENTICATED); 306 sendServerAuthToClient(); 307 mCurrentUnlockState = UNLOCK_STATE_MUTUAL_AUTH_ESTABLISHED; 308 break; 309 case UNLOCK_STATE_MUTUAL_AUTH_ESTABLISHED: 310 if (mEncryptionKey == null) { 311 Log.e(TAG, "Current session key null. Unexpected at this stage: " 312 + mCurrentUnlockState); 313 // Clear the previous session key. Need to re-enroll the trusted device. 314 mTrustedDeviceService.clearEncryptionKey(mClientDeviceId); 315 resetUnlockStateOnFailure(); 316 return; 317 } 318 319 // Save the current session to be used for authenticating the next session 320 mTrustedDeviceService.saveEncryptionKey(mClientDeviceId, mEncryptionKey.asBytes()); 321 322 byte[] decryptedCredentials; 323 try { 324 decryptedCredentials = mEncryptionKey.decryptData(value); 325 } catch (SignatureException e) { 326 Log.e(TAG, "Could not decrypt phone credentials.", e); 327 resetUnlockStateOnFailure(); 328 return; 329 } 330 331 processCredentials(decryptedCredentials); 332 mCurrentUnlockState = UNLOCK_STATE_PHONE_CREDENTIALS_RECEIVED; 333 logUnlockEvent(UNLOCK_CREDENTIALS_RECEIVED); 334 335 // Let the phone know that the token was received. 336 sendAckToClient(/* isEncrypted = */ true); 337 break; 338 case UNLOCK_STATE_PHONE_CREDENTIALS_RECEIVED: 339 // Should never get here because the unlock process should be completed now. 340 Log.e(TAG, "Landed on unexpected state of credentials received."); 341 break; 342 default: 343 Log.e(TAG, "Encountered unexpected unlock state: " + mCurrentUnlockState); 344 } 345 } 346 sendAckToClient(boolean isEncrypted)347 private void sendAckToClient(boolean isEncrypted) { 348 // Let the phone know that the handle was received. 349 byte[] ack = isEncrypted ? mEncryptionKey.encryptData(ACKNOWLEDGEMENT_MESSAGE) 350 : ACKNOWLEDGEMENT_MESSAGE; 351 mCarTrustAgentBleManager.sendUnlockMessage(mRemoteUnlockDevice, ack, 352 OperationType.CLIENT_MESSAGE, /* isPayloadEncrypted= */ isEncrypted); 353 } 354 355 @Nullable convertToDeviceId(byte[] id)356 private String convertToDeviceId(byte[] id) { 357 // Validate if the id exists i.e., if the phone is enrolled already 358 UUID deviceId = Utils.bytesToUUID(id); 359 if (deviceId == null 360 || mTrustedDeviceService.getEncryptionKey(deviceId.toString()) == null) { 361 if (deviceId != null) { 362 Log.e(TAG, "Unknown phone connected: " + deviceId.toString()); 363 } 364 return null; 365 } 366 367 return deviceId.toString(); 368 } 369 processKeyExchangeHandshakeMessage(byte[] message)370 private void processKeyExchangeHandshakeMessage(byte[] message) throws HandshakeException { 371 switch (mEncryptionState) { 372 case HandshakeMessage.HandshakeState.UNKNOWN: 373 if (Log.isLoggable(TAG, Log.DEBUG)) { 374 Log.d(TAG, "Responding to handshake init request."); 375 } 376 377 mHandshakeMessage = mEncryptionRunner.respondToInitRequest(message); 378 mEncryptionState = mHandshakeMessage.getHandshakeState(); 379 mCarTrustAgentBleManager.sendUnlockMessage(mRemoteUnlockDevice, 380 mHandshakeMessage.getNextMessage(), 381 OperationType.ENCRYPTION_HANDSHAKE, 382 /* isPayloadEncrypted= */ false); 383 logUnlockEvent(UNLOCK_ENCRYPTION_STATE, mEncryptionState); 384 break; 385 386 case HandshakeMessage.HandshakeState.IN_PROGRESS: 387 if (Log.isLoggable(TAG, Log.DEBUG)) { 388 Log.d(TAG, "Continuing handshake."); 389 } 390 391 mHandshakeMessage = mEncryptionRunner.continueHandshake(message); 392 mEncryptionState = mHandshakeMessage.getHandshakeState(); 393 394 if (Log.isLoggable(TAG, Log.DEBUG)) { 395 Log.d(TAG, "Updated encryption state: " + mEncryptionState); 396 } 397 398 // The state is updated after a call to continueHandshake(). Thus, need to check 399 // if we're in the next stage. 400 if (mEncryptionState == HandshakeMessage.HandshakeState.VERIFICATION_NEEDED) { 401 logUnlockEvent(UNLOCK_ENCRYPTION_STATE, mEncryptionState); 402 showVerificationCode(); 403 return; 404 } 405 406 // control shouldn't get here with Ukey2 407 mCarTrustAgentBleManager.sendUnlockMessage(mRemoteUnlockDevice, 408 mHandshakeMessage.getNextMessage(), 409 OperationType.ENCRYPTION_HANDSHAKE, /*isPayloadEncrypted= */false); 410 break; 411 case HandshakeMessage.HandshakeState.VERIFICATION_NEEDED: 412 case HandshakeMessage.HandshakeState.FINISHED: 413 // Should never reach this case since this state should occur after a verification 414 // code has been accepted. But it should mean handshake is done and the message 415 // is one for the escrow token. Start Mutual Auth from server - compute MACs and 416 // send it over 417 showVerificationCode(); 418 break; 419 420 default: 421 Log.w(TAG, "Encountered invalid handshake state: " + mEncryptionState); 422 break; 423 } 424 } 425 426 /** 427 * Verify the handshake. 428 * TODO(b/134073741) combine this with the method in CarTrustAgentEnrollmentService and 429 * have this take a boolean to blindly confirm the numeric code. 430 */ showVerificationCode()431 private void showVerificationCode() { 432 HandshakeMessage handshakeMessage; 433 434 // Blindly accept the verification code. 435 try { 436 handshakeMessage = mEncryptionRunner.verifyPin(); 437 } catch (HandshakeException e) { 438 Log.e(TAG, "Verify pin failed for new keys - Unexpected"); 439 resetUnlockStateOnFailure(); 440 return; 441 } 442 443 if (handshakeMessage.getHandshakeState() != HandshakeMessage.HandshakeState.FINISHED) { 444 Log.e(TAG, "Handshake not finished after calling verify PIN. Instead got state: " 445 + handshakeMessage.getHandshakeState()); 446 resetUnlockStateOnFailure(); 447 return; 448 } 449 450 mEncryptionState = HandshakeMessage.HandshakeState.FINISHED; 451 mEncryptionKey = handshakeMessage.getKey(); 452 mCurrentContext = D2DConnectionContext.fromSavedSession(mEncryptionKey.asBytes()); 453 454 if (mClientDeviceId == null) { 455 resetUnlockStateOnFailure(); 456 return; 457 } 458 byte[] oldSessionKeyBytes = mTrustedDeviceService.getEncryptionKey(mClientDeviceId); 459 if (oldSessionKeyBytes == null) { 460 Log.e(TAG, 461 "Could not retrieve previous session keys! Have to re-enroll trusted device"); 462 resetUnlockStateOnFailure(); 463 return; 464 } 465 466 mPrevContext = D2DConnectionContext.fromSavedSession(oldSessionKeyBytes); 467 if (mPrevContext == null) { 468 resetUnlockStateOnFailure(); 469 return; 470 } 471 472 // Now wait for the phone to send its MAC. 473 mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_CLIENT_AUTH; 474 logUnlockEvent(WAITING_FOR_CLIENT_AUTH); 475 } 476 sendServerAuthToClient()477 private void sendServerAuthToClient() { 478 byte[] resumeBytes = computeMAC(mPrevContext, mCurrentContext, SERVER); 479 if (resumeBytes == null) { 480 return; 481 } 482 // send to client 483 mCarTrustAgentBleManager.sendUnlockMessage(mRemoteUnlockDevice, resumeBytes, 484 OperationType.CLIENT_MESSAGE, /* isPayloadEncrypted= */false); 485 } 486 487 @Nullable computeMAC(D2DConnectionContext previous, D2DConnectionContext next, byte[] info)488 private byte[] computeMAC(D2DConnectionContext previous, D2DConnectionContext next, 489 byte[] info) { 490 try { 491 SecretKeySpec inputKeyMaterial = new SecretKeySpec( 492 Utils.concatByteArrays(previous.getSessionUnique(), next.getSessionUnique()), 493 "" /* key type is just plain raw bytes */); 494 return CryptoOps.hkdf(inputKeyMaterial, RESUME, info); 495 } catch (NoSuchAlgorithmException | InvalidKeyException e) { 496 // Does not happen in practice 497 Log.e(TAG, "Compute MAC failed"); 498 return null; 499 } 500 } 501 authenticateClient(byte[] message)502 private boolean authenticateClient(byte[] message) { 503 if (message.length != RESUME_HMAC_LENGTH) { 504 Log.e(TAG, "failing because message.length is " + message.length); 505 return false; 506 } 507 return MessageDigest.isEqual(message, 508 computeMAC(mPrevContext, mCurrentContext, CLIENT)); 509 } 510 processCredentials(byte[] credentials)511 void processCredentials(byte[] credentials) { 512 if (mUnlockDelegate == null) { 513 if (Log.isLoggable(TAG, Log.DEBUG)) { 514 Log.d(TAG, "No Unlock delegate to notify of unlock credentials."); 515 } 516 return; 517 } 518 519 queueMessageForLog("processCredentials"); 520 521 PhoneCredentials phoneCredentials; 522 try { 523 phoneCredentials = PhoneCredentials.parseFrom(credentials); 524 } catch (InvalidProtocolBufferException e) { 525 Log.e(TAG, "Error parsing credentials protobuf.", e); 526 return; 527 } 528 529 byte[] handle = phoneCredentials.getHandle().toByteArray(); 530 531 mUnlockDelegate.onUnlockDataReceived( 532 mTrustedDeviceService.getUserHandleByTokenHandle(Utils.bytesToLong(handle)), 533 phoneCredentials.getEscrowToken().toByteArray(), 534 Utils.bytesToLong(handle)); 535 } 536 537 /** 538 * Reset the whole unlock state. Disconnects from the peer device 539 * 540 * <p>This method should be called from any stage in the middle of unlock where we 541 * encounter a failure. 542 */ resetUnlockStateOnFailure()543 private void resetUnlockStateOnFailure() { 544 mCarTrustAgentBleManager.disconnectRemoteDevice(); 545 resetEncryptionState(); 546 } 547 548 /** 549 * Resets the encryption status of this service. 550 * 551 * <p>This method should be called each time a device connects so that a new handshake can be 552 * started and encryption keys exchanged. 553 */ resetEncryptionState()554 private void resetEncryptionState() { 555 mEncryptionRunner = EncryptionRunnerFactory.newRunner(); 556 mHandshakeMessage = null; 557 mEncryptionKey = null; 558 mEncryptionState = HandshakeMessage.HandshakeState.UNKNOWN; 559 mCurrentUnlockState = UNLOCK_STATE_WAITING_FOR_UNIQUE_ID; 560 if (mCurrentContext != null) { 561 mCurrentContext = null; 562 } 563 if (mPrevContext != null) { 564 mPrevContext = null; 565 } 566 } 567 dump(PrintWriter writer)568 void dump(PrintWriter writer) { 569 writer.println("*CarTrustAgentUnlockService*"); 570 writer.println("Unlock Service Logs:"); 571 for (String log : mLogQueue) { 572 writer.println("\t" + log); 573 } 574 } 575 queueMessageForLog(String message)576 private void queueMessageForLog(String message) { 577 if (mLogQueue.size() >= MAX_LOG_SIZE) { 578 mLogQueue.remove(); 579 } 580 mLogQueue.add(System.currentTimeMillis() + " : " + message); 581 } 582 } 583