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         // validity 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