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 android.car.Car.PERMISSION_CAR_ENROLL_TRUST;
20 import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_HANDSHAKE_FAILURE;
21 import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_NOT_ALLOWED;
22 
23 import static com.android.car.trust.EventLog.ENCRYPTION_KEY_SAVED;
24 import static com.android.car.trust.EventLog.ENROLLMENT_ENCRYPTION_STATE;
25 import static com.android.car.trust.EventLog.ENROLLMENT_HANDSHAKE_ACCEPTED;
26 import static com.android.car.trust.EventLog.ESCROW_TOKEN_ADDED;
27 import static com.android.car.trust.EventLog.RECEIVED_DEVICE_ID;
28 import static com.android.car.trust.EventLog.REMOTE_DEVICE_CONNECTED;
29 import static com.android.car.trust.EventLog.SHOW_VERIFICATION_CODE;
30 import static com.android.car.trust.EventLog.START_ENROLLMENT_ADVERTISING;
31 import static com.android.car.trust.EventLog.STOP_ENROLLMENT_ADVERTISING;
32 import static com.android.car.trust.EventLog.logEnrollmentEvent;
33 
34 import android.annotation.IntDef;
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.annotation.RequiresPermission;
38 import android.app.ActivityManager;
39 import android.bluetooth.BluetoothDevice;
40 import android.bluetooth.le.AdvertiseCallback;
41 import android.bluetooth.le.AdvertiseSettings;
42 import android.car.encryptionrunner.EncryptionRunner;
43 import android.car.encryptionrunner.EncryptionRunnerFactory;
44 import android.car.encryptionrunner.HandshakeException;
45 import android.car.encryptionrunner.HandshakeMessage;
46 import android.car.encryptionrunner.HandshakeMessage.HandshakeState;
47 import android.car.encryptionrunner.Key;
48 import android.car.trust.ICarTrustAgentBleCallback;
49 import android.car.trust.ICarTrustAgentEnrollment;
50 import android.car.trust.ICarTrustAgentEnrollmentCallback;
51 import android.car.trust.TrustedDeviceInfo;
52 import android.content.Context;
53 import android.content.SharedPreferences;
54 import android.os.IBinder;
55 import android.os.RemoteException;
56 import android.sysprop.CarProperties;
57 import android.util.Log;
58 
59 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
60 import com.android.car.ICarImpl;
61 import com.android.car.R;
62 import com.android.car.Utils;
63 import com.android.car.trust.CarTrustAgentBleManager.DataReceivedListener;
64 import com.android.car.trust.CarTrustAgentBleManager.SendMessageCallback;
65 import com.android.internal.annotations.GuardedBy;
66 import com.android.internal.annotations.VisibleForTesting;
67 
68 import java.io.PrintWriter;
69 import java.lang.annotation.Retention;
70 import java.lang.annotation.RetentionPolicy;
71 import java.security.SignatureException;
72 import java.util.ArrayList;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Iterator;
76 import java.util.LinkedList;
77 import java.util.List;
78 import java.util.Map;
79 import java.util.Queue;
80 import java.util.Set;
81 import java.util.UUID;
82 
83 /**
84  * A service that is part of the CarTrustedDeviceService that is responsible for allowing a
85  * phone to enroll as a trusted device.  The enrolled phone can then be used for authenticating a
86  * user on the HU.  This implements the {@link android.car.trust.CarTrustAgentEnrollmentManager}
87  * APIs that an app like Car Settings can call to conduct an enrollment.
88  *
89  * @deprecated Enrollment of a trusted device is no longer a supported feature of car service and
90  * these APIs will be removed in the next Android release.
91  */
92 @Deprecated
93 public class CarTrustAgentEnrollmentService extends ICarTrustAgentEnrollment.Stub implements
94         DataReceivedListener {
95     private static final String TAG = "CarTrustAgentEnroll";
96     private static final String TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY =
97             "trusted_device_enrollment_enabled";
98     @VisibleForTesting
99     static final byte[] CONFIRMATION_SIGNAL = "True".getBytes();
100     //Arbirary log size
101     private static final int MAX_LOG_SIZE = 20;
102     // This delimiter separates deviceId and deviceInfo, so it has to differ from the
103     // TrustedDeviceInfo delimiter. Once new API can be added, deviceId will be added to
104     // TrustedDeviceInfo and this delimiter will be removed.
105     private static final char DEVICE_INFO_DELIMITER = '#';
106 
107     // Device name length is limited by available bytes in BLE advertisement data packet.
108     //
109     // BLE advertisement limits data packet length to 31
110     // Currently we send:
111     // - 18 bytes for 16 chars UUID: 16 bytes + 2 bytes for header;
112     // - 3 bytes for advertisement being connectable;
113     // which leaves 10 bytes.
114     // Subtracting 2 bytes used by header, we have 8 bytes for device name.
115     private static final int DEVICE_NAME_LENGTH_LIMIT = 8;
116     // Limit prefix to 4 chars and fill the rest with randomly generated name. Use random name
117     // to improve uniqueness in paired device name.
118     private static final int DEVICE_NAME_PREFIX_LIMIT = 4;
119 
120     private final CarTrustedDeviceService mTrustedDeviceService;
121     private final CarCompanionDeviceStorage mCarCompanionDeviceStorage;
122     // List of clients listening to Enrollment state change events.
123     private final List<EnrollmentStateClient> mEnrollmentStateClients = new ArrayList<>();
124     // List of clients listening to BLE state changes events during enrollment.
125     private final List<BleStateChangeClient> mBleStateChangeClients = new ArrayList<>();
126     private final Queue<String> mLogQueue = new LinkedList<>();
127 
128     private final CarTrustAgentBleManager mCarTrustAgentBleManager;
129     private CarTrustAgentEnrollmentRequestDelegate mEnrollmentDelegate;
130 
131     private Object mRemoteDeviceLock = new Object();
132     @GuardedBy("mRemoteDeviceLock")
133     private BluetoothDevice mRemoteEnrollmentDevice;
134     private final Map<Long, Boolean> mTokenActiveStateMap = new HashMap<>();
135     private String mClientDeviceName;
136     private String mClientDeviceId;
137     private final UUID mEnrollmentClientWriteUuid;
138     private final Context mContext;
139     private String mEnrollmentDeviceName;
140     private SendMessageCallback mSendMessageCallback = this::terminateEnrollmentHandshake;
141 
142     private EncryptionRunner mEncryptionRunner = EncryptionRunnerFactory.newRunner();
143     private HandshakeMessage mHandshakeMessage;
144     private Key mEncryptionKey;
145     private long mHandle;
146     @VisibleForTesting
147     @HandshakeState
148     int mEncryptionState = HandshakeState.UNKNOWN;
149     // State of last message sent to phone in enrollment process. Order matters with
150     // state being auto-incremented.
151     static final int ENROLLMENT_STATE_NONE = 0;
152     static final int ENROLLMENT_STATE_UNIQUE_ID = 1;
153     static final int ENROLLMENT_STATE_ENCRYPTION_COMPLETED = 2;
154     static final int ENROLLMENT_STATE_HANDLE = 3;
155 
156     /** @hide */
157     @VisibleForTesting
158     @Retention(RetentionPolicy.SOURCE)
159     @IntDef({ENROLLMENT_STATE_NONE, ENROLLMENT_STATE_UNIQUE_ID,
160             ENROLLMENT_STATE_ENCRYPTION_COMPLETED, ENROLLMENT_STATE_HANDLE})
161     @interface EnrollmentState {
162     }
163 
164     @VisibleForTesting
165     @EnrollmentState
166     int mEnrollmentState;
167 
CarTrustAgentEnrollmentService(Context context, CarTrustedDeviceService service, CarTrustAgentBleManager bleService)168     public CarTrustAgentEnrollmentService(Context context, CarTrustedDeviceService service,
169             CarTrustAgentBleManager bleService) {
170         mContext = context;
171         mTrustedDeviceService = service;
172         mCarTrustAgentBleManager = bleService;
173         mEnrollmentClientWriteUuid = UUID.fromString(context
174                 .getString(R.string.enrollment_client_write_uuid));
175         mCarCompanionDeviceStorage = new CarCompanionDeviceStorage(context);
176     }
177 
init()178     public synchronized void init() {
179         mCarTrustAgentBleManager.setupEnrollmentBleServer();
180         mCarTrustAgentBleManager.addDataReceivedListener(mEnrollmentClientWriteUuid,
181                 this);
182     }
183 
184     /**
185      * Pass a dummy encryption to generate a dummy key, only for test purpose.
186      */
187     @VisibleForTesting
setEncryptionRunner(EncryptionRunner dummyEncryptionRunner)188     void setEncryptionRunner(EncryptionRunner dummyEncryptionRunner) {
189         mEncryptionRunner = dummyEncryptionRunner;
190     }
191 
release()192     public synchronized void release() {
193         for (EnrollmentStateClient client : mEnrollmentStateClients) {
194             client.mListenerBinder.unlinkToDeath(client, 0);
195         }
196         for (BleStateChangeClient client : mBleStateChangeClients) {
197             client.mListenerBinder.unlinkToDeath(client, 0);
198         }
199         mEnrollmentStateClients.clear();
200     }
201 
202     // Implementing the ICarTrustAgentEnrollment interface
203 
204     /**
205      * Begin BLE advertisement for Enrollment. This should be called from an app that conducts
206      * the enrollment of the trusted device.
207      */
208     @Override
209     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
startEnrollmentAdvertising()210     public void startEnrollmentAdvertising() {
211         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
212         if (!mCarCompanionDeviceStorage.getSharedPrefs()
213                 .getBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, true)) {
214             Log.e(TAG, "Trusted Device Enrollment disabled");
215             dispatchEnrollmentFailure(ENROLLMENT_NOT_ALLOWED);
216             return;
217         }
218         // Stop any current broadcasts
219         mTrustedDeviceService.getCarTrustAgentUnlockService().stopUnlockAdvertising();
220         stopEnrollmentAdvertising();
221 
222         logEnrollmentEvent(START_ENROLLMENT_ADVERTISING);
223         addEnrollmentServiceLog("startEnrollmentAdvertising");
224         mCarTrustAgentBleManager.startEnrollmentAdvertising(getEnrollmentDeviceName(),
225                 new AdvertiseCallback() {
226                     @Override
227                     public void onStartSuccess(AdvertiseSettings settingsInEffect) {
228                         super.onStartSuccess(settingsInEffect);
229                         onEnrollmentAdvertiseStartSuccess();
230                         if (Log.isLoggable(TAG, Log.DEBUG)) {
231                             Log.d(TAG, "Successfully started advertising service");
232                         }
233                     }
234 
235                     @Override
236                     public void onStartFailure(int errorCode) {
237                         super.onStartFailure(errorCode);
238                         Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
239                         onEnrollmentAdvertiseStartFailure();
240                     }
241             });
242         mEnrollmentState = ENROLLMENT_STATE_NONE;
243     }
244 
245     /**
246      * Stop BLE advertisement for Enrollment
247      */
248     @Override
249     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
stopEnrollmentAdvertising()250     public void stopEnrollmentAdvertising() {
251         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
252         logEnrollmentEvent(STOP_ENROLLMENT_ADVERTISING);
253         addEnrollmentServiceLog("stopEnrollmentAdvertising");
254         mCarTrustAgentBleManager.stopEnrollmentAdvertising();
255     }
256 
257     /**
258      * Called by the client to notify that the user has accepted a pairing code or any out-of-band
259      * confirmation, and send confirmation signals to remote bluetooth device.
260      *
261      * @param device the remote Bluetooth device that will receive the signal.
262      */
263     @Override
264     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
enrollmentHandshakeAccepted(BluetoothDevice device)265     public void enrollmentHandshakeAccepted(BluetoothDevice device) {
266         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
267         logEnrollmentEvent(ENROLLMENT_HANDSHAKE_ACCEPTED);
268         addEnrollmentServiceLog("enrollmentHandshakeAccepted");
269         if (device == null || !device.equals(mRemoteEnrollmentDevice)) {
270             Log.wtf(TAG,
271                     "Enrollment Failure: device is different from cached remote bluetooth device,"
272                             + " disconnect from the device. current device is:" + device);
273             mCarTrustAgentBleManager.disconnectRemoteDevice();
274             return;
275         }
276         mCarTrustAgentBleManager.sendMessage(CONFIRMATION_SIGNAL,
277                 OperationType.ENCRYPTION_HANDSHAKE, /* isPayloadEncrypted= */ false,
278                 mSendMessageCallback);
279         setEnrollmentHandshakeAccepted();
280     }
281 
282     /**
283      * Terminate the Enrollment process.  To be called when an error is encountered during
284      * enrollment.  For example - user pressed cancel on pairing code confirmation or user
285      * navigated away from the app before completing enrollment.
286      */
287     @Override
288     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
terminateEnrollmentHandshake()289     public void terminateEnrollmentHandshake() {
290         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
291         addEnrollmentServiceLog("terminateEnrollmentHandshake");
292         // Disconnect from BLE
293         mCarTrustAgentBleManager.disconnectRemoteDevice();
294         // Remove any handles that have not been activated yet.
295         Iterator<Map.Entry<Long, Boolean>> it = mTokenActiveStateMap.entrySet().iterator();
296         while (it.hasNext()) {
297             Map.Entry<Long, Boolean> pair = it.next();
298             boolean isHandleActive = pair.getValue();
299             if (!isHandleActive) {
300                 long handle = pair.getKey();
301                 int uid = mCarCompanionDeviceStorage.getSharedPrefs().getInt(String.valueOf(handle),
302                         -1);
303                 removeEscrowToken(handle, uid);
304                 it.remove();
305             }
306         }
307     }
308 
309     /*
310      * Returns if there is an active token for the given user and handle.
311      *
312      * @param handle handle corresponding to the escrow token
313      * @param uid    user id
314      * @return True if the escrow token is active, false if not
315      */
316     @Override
317     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
isEscrowTokenActive(long handle, int uid)318     public boolean isEscrowTokenActive(long handle, int uid) {
319         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
320         if (mTokenActiveStateMap.get(handle) != null) {
321             return mTokenActiveStateMap.get(handle);
322         }
323         return false;
324     }
325 
326     /**
327      * Remove the Token associated with the given handle for the given user.
328      *
329      * @param handle handle corresponding to the escrow token
330      * @param uid    user id
331      */
332     @Override
333     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
removeEscrowToken(long handle, int uid)334     public void removeEscrowToken(long handle, int uid) {
335         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
336         mEnrollmentDelegate.removeEscrowToken(handle, uid);
337         addEnrollmentServiceLog("removeEscrowToken (handle:" + handle + " uid:" + uid + ")");
338     }
339 
340     /**
341      * Remove all Trusted devices associated with the given user.
342      *
343      * @param uid user id
344      */
345     @Override
346     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
removeAllTrustedDevices(int uid)347     public void removeAllTrustedDevices(int uid) {
348         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
349         for (TrustedDeviceInfo device : getEnrolledDeviceInfosForUser(uid)) {
350             removeEscrowToken(device.getHandle(), uid);
351         }
352     }
353 
354     /**
355      * Enable or disable enrollment of a Trusted device.  When disabled,
356      * {@link android.car.trust.CarTrustAgentEnrollmentManager#ENROLLMENT_NOT_ALLOWED} is returned,
357      * when {@link #startEnrollmentAdvertising()} is called by a client.
358      *
359      * @param isEnabled {@code true} to enable; {@code false} to disable the feature.
360      */
361     @Override
362     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
setTrustedDeviceEnrollmentEnabled(boolean isEnabled)363     public void setTrustedDeviceEnrollmentEnabled(boolean isEnabled) {
364         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
365         SharedPreferences.Editor editor = mCarCompanionDeviceStorage.getSharedPrefs().edit();
366         editor.putBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, isEnabled);
367         if (!editor.commit()) {
368             Log.wtf(TAG,
369                     "Enrollment Failure: Commit to SharedPreferences failed. Enable? " + isEnabled);
370         }
371     }
372 
373     /**
374      * Enable or disable authentication of the head unit with a trusted device.
375      *
376      * @param isEnabled when set to {@code false}, head unit will not be
377      *                  discoverable to unlock the user.  Setting it to {@code true} will enable it
378      *                  back.
379      */
380     @Override
381     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
setTrustedDeviceUnlockEnabled(boolean isEnabled)382     public void setTrustedDeviceUnlockEnabled(boolean isEnabled) {
383         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
384         mTrustedDeviceService.getCarTrustAgentUnlockService()
385                 .setTrustedDeviceUnlockEnabled(isEnabled);
386     }
387 
388     /**
389      * Get the Handles and Device Mac Address corresponding to the token for the current user.  The
390      * client can use this to list the trusted devices for the user.
391      *
392      * @param uid user id
393      * @return array of trusted device handles and names for the user.
394      */
395     @NonNull
396     @Override
397     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
getEnrolledDeviceInfosForUser(int uid)398     public List<TrustedDeviceInfo> getEnrolledDeviceInfosForUser(int uid) {
399         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
400         Set<String> enrolledDeviceInfos = mCarCompanionDeviceStorage.getSharedPrefs().getStringSet(
401                 String.valueOf(uid), new HashSet<>());
402         List<TrustedDeviceInfo> trustedDeviceInfos = new ArrayList<>(enrolledDeviceInfos.size());
403         for (String deviceInfoWithId : enrolledDeviceInfos) {
404             TrustedDeviceInfo deviceInfo = extractDeviceInfo(deviceInfoWithId);
405             if (deviceInfo != null) {
406                 trustedDeviceInfos.add(deviceInfo);
407             }
408         }
409         return trustedDeviceInfos;
410     }
411 
412     /**
413      * Registers a {@link ICarTrustAgentEnrollmentCallback} to be notified for changes to the
414      * enrollment state.
415      *
416      * @param listener {@link ICarTrustAgentEnrollmentCallback}
417      */
418     @Override
419     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener)420     public synchronized void registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener) {
421         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
422         if (listener == null) {
423             throw new IllegalArgumentException("Listener is null");
424         }
425         // If a new client is registering, create a new EnrollmentStateClient and add it to the list
426         // of listening clients.
427         EnrollmentStateClient client = findEnrollmentStateClientLocked(listener);
428         if (client == null) {
429             client = new EnrollmentStateClient(listener);
430             try {
431                 listener.asBinder().linkToDeath(client, 0);
432             } catch (RemoteException e) {
433                 Log.e(TAG, "Cannot link death recipient to binder ", e);
434                 return;
435             }
436             mEnrollmentStateClients.add(client);
437         }
438     }
439 
440     /**
441      * Called after the escrow token has been successfully added to the framework.
442      *
443      * @param token  the escrow token which has been added
444      * @param handle the given handle of that token
445      * @param uid    the current user id
446      */
onEscrowTokenAdded(byte[] token, long handle, int uid)447     void onEscrowTokenAdded(byte[] token, long handle, int uid) {
448         if (Log.isLoggable(TAG, Log.DEBUG)) {
449             Log.d(TAG, "onEscrowTokenAdded handle:" + handle + " uid:" + uid);
450         }
451 
452         if (mRemoteEnrollmentDevice == null) {
453             Log.e(TAG, "onEscrowTokenAdded() but no remote device connected!");
454             removeEscrowToken(handle, uid);
455             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
456             return;
457         }
458 
459         mTokenActiveStateMap.put(handle, false);
460         for (EnrollmentStateClient client : mEnrollmentStateClients) {
461             try {
462                 client.mListener.onEscrowTokenAdded(handle);
463             } catch (RemoteException e) {
464                 Log.e(TAG, "onEscrowTokenAdded dispatch failed", e);
465             }
466         }
467     }
468 
469     /**
470      * Called after the escrow token has been successfully removed from the framework.
471      */
onEscrowTokenRemoved(long handle, int uid)472     void onEscrowTokenRemoved(long handle, int uid) {
473         if (Log.isLoggable(TAG, Log.DEBUG)) {
474             Log.d(TAG, "onEscrowTokenRemoved handle:" + handle + " uid:" + uid);
475         }
476         for (EnrollmentStateClient client : mEnrollmentStateClients) {
477             try {
478                 client.mListener.onEscrowTokenRemoved(handle);
479             } catch (RemoteException e) {
480                 Log.e(TAG, "onEscrowTokenRemoved dispatch failed", e);
481             }
482         }
483         SharedPreferences sharedPrefs = mCarCompanionDeviceStorage.getSharedPrefs();
484         SharedPreferences.Editor editor = sharedPrefs.edit();
485         editor.remove(String.valueOf(handle));
486         Set<String> deviceInfos = sharedPrefs.getStringSet(String.valueOf(uid), new HashSet<>());
487         Iterator<String> iterator = deviceInfos.iterator();
488         while (iterator.hasNext()) {
489             String deviceIdAndInfo = iterator.next();
490             TrustedDeviceInfo info = extractDeviceInfo(deviceIdAndInfo);
491             if (info != null && info.getHandle() == handle) {
492                 if (Log.isLoggable(TAG, Log.DEBUG)) {
493                     Log.d(TAG, "Removing trusted device: " + info);
494                 }
495                 String clientDeviceId = extractDeviceId(deviceIdAndInfo);
496                 if (clientDeviceId != null && sharedPrefs.getLong(clientDeviceId, -1) == handle) {
497                     editor.remove(clientDeviceId);
498                 }
499                 iterator.remove();
500                 break;
501             }
502         }
503         editor.putStringSet(String.valueOf(uid), deviceInfos);
504         if (!editor.commit()) {
505             Log.e(TAG, "EscrowToken removed, but shared prefs update failed");
506         }
507     }
508 
509     /**
510      * @param handle        the handle whose active state change
511      * @param isTokenActive the active state of the handle
512      * @param uid           id of current user
513      */
onEscrowTokenActiveStateChanged(long handle, boolean isTokenActive, int uid)514     void onEscrowTokenActiveStateChanged(long handle, boolean isTokenActive, int uid) {
515         if (Log.isLoggable(TAG, Log.DEBUG)) {
516             Log.d(TAG, "onEscrowTokenActiveStateChanged: " + Long.toHexString(handle));
517         }
518         if (mRemoteEnrollmentDevice == null || !isTokenActive) {
519             if (mRemoteEnrollmentDevice == null) {
520                 Log.e(TAG,
521                         "Device disconnected before sending back handle.  Enrollment incomplete");
522             }
523             if (!isTokenActive) {
524                 Log.e(TAG, "Unexpected: Escrow Token activation failed");
525             }
526             removeEscrowToken(handle, uid);
527             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
528             return;
529         }
530 
531         // Avoid storing duplicate info for same device by checking if there is already device info
532         // and deleting it.
533         SharedPreferences sharedPrefs = mCarCompanionDeviceStorage.getSharedPrefs();
534         if (sharedPrefs.contains(mClientDeviceId)) {
535             removeEscrowToken(sharedPrefs.getLong(mClientDeviceId, -1), uid);
536         }
537         mTokenActiveStateMap.put(handle, isTokenActive);
538         Set<String> deviceInfo = sharedPrefs.getStringSet(String.valueOf(uid), new HashSet<>());
539         String clientDeviceName;
540         if (mRemoteEnrollmentDevice.getName() != null) {
541             clientDeviceName = mRemoteEnrollmentDevice.getName();
542         } else if (mClientDeviceName != null) {
543             clientDeviceName = mClientDeviceName;
544         } else {
545             clientDeviceName = mContext.getString(R.string.trust_device_default_name);
546         }
547         StringBuffer log = new StringBuffer()
548                 .append("trustedDeviceAdded (id:").append(mClientDeviceId)
549                 .append(", handle:").append(handle)
550                 .append(", uid:").append(uid)
551                 .append(", addr:").append(mRemoteEnrollmentDevice.getAddress())
552                 .append(", name:").append(clientDeviceName).append(")");
553         addEnrollmentServiceLog(log.toString());
554         deviceInfo.add(serializeDeviceInfoWithId(new TrustedDeviceInfo(handle,
555                 mRemoteEnrollmentDevice.getAddress(), clientDeviceName), mClientDeviceId));
556 
557         // To conveniently get the devices info regarding certain user.
558         SharedPreferences.Editor editor = sharedPrefs.edit();
559         editor.putStringSet(String.valueOf(uid), deviceInfo);
560         if (!editor.commit()) {
561             Log.e(TAG, "Writing DeviceInfo to shared prefs Failed");
562             removeEscrowToken(handle, uid);
563             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
564             return;
565         }
566 
567         // To conveniently get the user id to unlock when handle is received.
568         editor.putInt(String.valueOf(handle), uid);
569         if (!editor.commit()) {
570             Log.e(TAG, "Writing (handle, uid) to shared prefs Failed");
571             removeEscrowToken(handle, uid);
572             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
573             return;
574         }
575 
576         // To check if the device has already been mapped to a handle
577         editor.putLong(mClientDeviceId, handle);
578         if (!editor.commit()) {
579             Log.e(TAG, "Writing (identifier, handle) to shared prefs Failed");
580             removeEscrowToken(handle, uid);
581             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
582             return;
583         }
584 
585         if (Log.isLoggable(TAG, Log.DEBUG)) {
586             Log.d(TAG, "Sending handle: " + handle);
587         }
588         mHandle = handle;
589         mCarTrustAgentBleManager.sendMessage(
590                 mEncryptionKey.encryptData(Utils.longToBytes(handle)),
591                 OperationType.CLIENT_MESSAGE, /* isPayloadEncrypted= */ true,
592                 mSendMessageCallback);
593     }
594 
onEnrollmentAdvertiseStartSuccess()595     void onEnrollmentAdvertiseStartSuccess() {
596         for (BleStateChangeClient client : mBleStateChangeClients) {
597             try {
598                 client.mListener.onEnrollmentAdvertisingStarted();
599             } catch (RemoteException e) {
600                 Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
601             }
602         }
603     }
604 
onEnrollmentAdvertiseStartFailure()605     void onEnrollmentAdvertiseStartFailure() {
606         for (BleStateChangeClient client : mBleStateChangeClients) {
607             try {
608                 client.mListener.onEnrollmentAdvertisingFailed();
609             } catch (RemoteException e) {
610                 Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
611             }
612         }
613     }
614 
615     /**
616      * Called when a device has been connected through bluetooth
617      *
618      * @param device the connected device
619      */
onRemoteDeviceConnected(BluetoothDevice device)620     void onRemoteDeviceConnected(BluetoothDevice device) {
621         logEnrollmentEvent(REMOTE_DEVICE_CONNECTED);
622         addEnrollmentServiceLog("onRemoteDeviceConnected (addr:" + device.getAddress() + ")");
623         resetEncryptionState();
624         mHandle = 0;
625         synchronized (mRemoteDeviceLock) {
626             mRemoteEnrollmentDevice = device;
627         }
628         for (BleStateChangeClient client : mBleStateChangeClients) {
629             try {
630                 client.mListener.onBleEnrollmentDeviceConnected(device);
631             } catch (RemoteException e) {
632                 Log.e(TAG, "onRemoteDeviceConnected dispatch failed", e);
633             }
634         }
635         mCarTrustAgentBleManager.stopEnrollmentAdvertising();
636     }
637 
onRemoteDeviceDisconnected(BluetoothDevice device)638     void onRemoteDeviceDisconnected(BluetoothDevice device) {
639         if (Log.isLoggable(TAG, Log.DEBUG)) {
640             Log.d(TAG, "Device Disconnected: " + device.getAddress() + " Enrollment State: "
641                     + mEnrollmentState + " Encryption State: " + mEncryptionState);
642         }
643         addEnrollmentServiceLog("onRemoteDeviceDisconnected (addr:" + device.getAddress() + ")");
644         addEnrollmentServiceLog(
645                 "Enrollment State: " + mEnrollmentState + " EncryptionState: " + mEncryptionState);
646         resetEncryptionState();
647         mHandle = 0;
648         synchronized (mRemoteDeviceLock) {
649             mRemoteEnrollmentDevice = null;
650         }
651         for (BleStateChangeClient client : mBleStateChangeClients) {
652             try {
653                 client.mListener.onBleEnrollmentDeviceDisconnected(device);
654             } catch (RemoteException e) {
655                 Log.e(TAG, "onRemoteDeviceDisconnected dispatch failed", e);
656             }
657         }
658     }
659 
660     /**
661      * Called when data is received during enrollment process.
662      *
663      * @param value received data
664      */
665     @Override
onDataReceived(byte[] value)666     public void onDataReceived(byte[] value) {
667         if (mEnrollmentDelegate == null) {
668             if (Log.isLoggable(TAG, Log.DEBUG)) {
669                 Log.d(TAG, "Enrollment Delegate not set");
670             }
671             return;
672         }
673         switch (mEnrollmentState) {
674             case ENROLLMENT_STATE_NONE:
675                 if (!CarTrustAgentValidator.isValidEnrollmentDeviceId(value)) {
676                     Log.e(TAG, "Device id rejected by validator.");
677                     return;
678                 }
679                 notifyDeviceIdReceived(value);
680                 logEnrollmentEvent(RECEIVED_DEVICE_ID);
681                 break;
682             case ENROLLMENT_STATE_UNIQUE_ID:
683                 try {
684                     processInitEncryptionMessage(value);
685                 } catch (HandshakeException e) {
686                     Log.e(TAG, "HandshakeException during set up of encryption: ", e);
687                 }
688                 break;
689             case ENROLLMENT_STATE_ENCRYPTION_COMPLETED:
690                 notifyEscrowTokenReceived(value);
691                 break;
692             case ENROLLMENT_STATE_HANDLE:
693                 // only activated handle can be sent to the connected remote device.
694                 dispatchEscrowTokenActiveStateChanged(mHandle, true);
695                 mCarTrustAgentBleManager.disconnectRemoteDevice();
696                 break;
697             default:
698                 // Should never get here
699                 break;
700         }
701     }
702 
onClientDeviceNameRetrieved(String deviceName)703     void onClientDeviceNameRetrieved(String deviceName) {
704         mClientDeviceName = deviceName;
705     }
706 
notifyDeviceIdReceived(byte[] id)707     private void notifyDeviceIdReceived(byte[] id) {
708         UUID deviceId = Utils.bytesToUUID(id);
709         if (deviceId == null) {
710             Log.e(TAG, "Invalid device id sent");
711             return;
712         }
713         mClientDeviceId = deviceId.toString();
714         if (Log.isLoggable(TAG, Log.DEBUG)) {
715             Log.d(TAG, "Received device id: " + mClientDeviceId);
716         }
717         UUID uniqueId = mCarCompanionDeviceStorage.getUniqueId();
718         if (uniqueId == null) {
719             Log.e(TAG, "Cannot get Unique ID for the IHU");
720             resetEnrollmentStateOnFailure();
721             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
722             return;
723         }
724         if (Log.isLoggable(TAG, Log.DEBUG)) {
725             Log.d(TAG, "Sending device id: " + uniqueId.toString());
726         }
727         mCarTrustAgentBleManager.sendMessage(
728                 Utils.uuidToBytes(uniqueId), OperationType.CLIENT_MESSAGE,
729                 /* isPayloadEncrypted= */ false,
730                 mSendMessageCallback);
731         mEnrollmentState++;
732     }
733 
notifyEscrowTokenReceived(byte[] token)734     private void notifyEscrowTokenReceived(byte[] token) {
735         try {
736             mEnrollmentDelegate.addEscrowToken(
737                     mEncryptionKey.decryptData(token), ActivityManager.getCurrentUser());
738             mEnrollmentState++;
739             logEnrollmentEvent(ESCROW_TOKEN_ADDED);
740         } catch (SignatureException e) {
741             Log.e(TAG, "Could not decrypt escrow token", e);
742         }
743     }
744 
745     /**
746      * Processes the given message as one that will establish encryption for secure communication.
747      *
748      * <p>This method should be called continually until {@link #mEncryptionState} is
749      * {@link HandshakeState#FINISHED}, meaning an secure channel has been set up.
750      *
751      * @param message The message received from the connected device.
752      * @throws HandshakeException If an error was encountered during the handshake flow.
753      */
processInitEncryptionMessage(byte[] message)754     private void processInitEncryptionMessage(byte[] message) throws HandshakeException {
755         if (Log.isLoggable(TAG, Log.DEBUG)) {
756             Log.d(TAG, "Processing init encryption message.");
757         }
758         switch (mEncryptionState) {
759             case HandshakeState.UNKNOWN:
760                 if (Log.isLoggable(TAG, Log.DEBUG)) {
761                     Log.d(TAG, "Responding to handshake init request.");
762                 }
763 
764                 mHandshakeMessage = mEncryptionRunner.respondToInitRequest(message);
765                 mEncryptionState = mHandshakeMessage.getHandshakeState();
766                 mCarTrustAgentBleManager.sendMessage(
767                         mHandshakeMessage.getNextMessage(),
768                         OperationType.ENCRYPTION_HANDSHAKE, /* isPayloadEncrypted= */ false,
769                         mSendMessageCallback);
770 
771                 logEnrollmentEvent(ENROLLMENT_ENCRYPTION_STATE, mEncryptionState);
772                 break;
773 
774             case HandshakeState.IN_PROGRESS:
775                 if (Log.isLoggable(TAG, Log.DEBUG)) {
776                     Log.d(TAG, "Continuing handshake.");
777                 }
778 
779                 mHandshakeMessage = mEncryptionRunner.continueHandshake(message);
780                 mEncryptionState = mHandshakeMessage.getHandshakeState();
781 
782                 if (Log.isLoggable(TAG, Log.DEBUG)) {
783                     Log.d(TAG, "Updated encryption state: " + mEncryptionState);
784                 }
785 
786                 // The state is updated after a call to continueHandshake(). Thus, need to check
787                 // if we're in the next stage.
788                 if (mEncryptionState == HandshakeState.VERIFICATION_NEEDED) {
789                     showVerificationCode();
790                     return;
791                 }
792                 mCarTrustAgentBleManager.sendMessage(
793                         mHandshakeMessage.getNextMessage(), OperationType.ENCRYPTION_HANDSHAKE,
794                         /* isPayloadEncrypted= */ false, mSendMessageCallback);
795                 break;
796             case HandshakeState.VERIFICATION_NEEDED:
797                 Log.w(TAG, "Encountered VERIFICATION_NEEDED state when it should have been "
798                         + "transitioned to after IN_PROGRESS.");
799                 // This case should never happen because this state should occur right after
800                 // a call to "continueHandshake". But just in case, call the appropriate method.
801                 showVerificationCode();
802                 break;
803 
804             case HandshakeState.FINISHED:
805                 // Should never reach this case since this state should occur after a verification
806                 // code has been accepted. But it should mean handshake is done and the message
807                 // is one for the escrow token.
808                 notifyEscrowTokenReceived(message);
809                 break;
810 
811             default:
812                 Log.w(TAG, "Encountered invalid handshake state: " + mEncryptionState);
813                 break;
814         }
815     }
816 
showVerificationCode()817     private void showVerificationCode() {
818         if (Log.isLoggable(TAG, Log.DEBUG)) {
819             Log.d(TAG, "showVerificationCode(): " + mHandshakeMessage.getVerificationCode());
820         }
821 
822         for (EnrollmentStateClient client : mEnrollmentStateClients) {
823             try {
824                 client.mListener.onAuthStringAvailable(mRemoteEnrollmentDevice,
825                         mHandshakeMessage.getVerificationCode());
826             } catch (RemoteException e) {
827                 Log.e(TAG, "Broadcast verification code failed", e);
828             }
829         }
830         logEnrollmentEvent(SHOW_VERIFICATION_CODE);
831     }
832 
833     /**
834      * Reset the whole enrollment state.  Disconnects the peer device and removes any escrow token
835      * that has not been activated.
836      *
837      * <p>This method should be called from any stage in the middle of enrollment where we
838      * encounter a failure.
839      */
resetEnrollmentStateOnFailure()840     private void resetEnrollmentStateOnFailure() {
841         terminateEnrollmentHandshake();
842         resetEncryptionState();
843     }
844 
845     /**
846      * Resets the encryption status of this service.
847      *
848      * <p>This method should be called each time a device connects so that a new handshake can be
849      * started and encryption keys exchanged.
850      */
resetEncryptionState()851     private void resetEncryptionState() {
852         mEncryptionRunner = EncryptionRunnerFactory.newRunner();
853         mHandshakeMessage = null;
854         mEncryptionKey = null;
855         mEncryptionState = HandshakeState.UNKNOWN;
856         mEnrollmentState = ENROLLMENT_STATE_NONE;
857     }
858 
setEnrollmentHandshakeAccepted()859     private synchronized void setEnrollmentHandshakeAccepted() {
860         if (mEncryptionRunner == null) {
861             Log.e(TAG, "Received notification that enrollment handshake was accepted, "
862                     + "but encryption was never set up.");
863             return;
864         }
865         HandshakeMessage message;
866         try {
867             message = mEncryptionRunner.verifyPin();
868         } catch (HandshakeException e) {
869             Log.e(TAG, "Error during PIN verification", e);
870             resetEnrollmentStateOnFailure();
871             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
872             return;
873         }
874 
875         if (message.getHandshakeState() != HandshakeState.FINISHED) {
876             Log.e(TAG, "Handshake not finished after calling verify PIN. Instead got state: "
877                     + message.getHandshakeState());
878             return;
879         }
880 
881         mEncryptionState = HandshakeState.FINISHED;
882         mEncryptionKey = message.getKey();
883         if (!mCarCompanionDeviceStorage.saveEncryptionKey(mClientDeviceId,
884                 mEncryptionKey.asBytes())) {
885             resetEnrollmentStateOnFailure();
886             dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE);
887             return;
888         }
889         logEnrollmentEvent(ENCRYPTION_KEY_SAVED);
890         mEnrollmentState++;
891     }
892 
893     /**
894      * Iterates through the list of registered Enrollment State Change clients -
895      * {@link EnrollmentStateClient} and finds if the given client is already registered.
896      *
897      * @param listener Listener to look for.
898      * @return the {@link EnrollmentStateClient} if found, null if not
899      */
900     @Nullable
findEnrollmentStateClientLocked( ICarTrustAgentEnrollmentCallback listener)901     private EnrollmentStateClient findEnrollmentStateClientLocked(
902             ICarTrustAgentEnrollmentCallback listener) {
903         IBinder binder = listener.asBinder();
904         // Find the listener by comparing the binder object they host.
905         for (EnrollmentStateClient client : mEnrollmentStateClients) {
906             if (client.isHoldingBinder(binder)) {
907                 return client;
908             }
909         }
910         return null;
911     }
912 
913     /**
914      * Unregister the given Enrollment State Change listener
915      *
916      * @param listener client to unregister
917      */
918     @Override
919     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
unregisterEnrollmentCallback( ICarTrustAgentEnrollmentCallback listener)920     public synchronized void unregisterEnrollmentCallback(
921             ICarTrustAgentEnrollmentCallback listener) {
922         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
923         if (listener == null) {
924             throw new IllegalArgumentException("Listener is null");
925         }
926 
927         EnrollmentStateClient client = findEnrollmentStateClientLocked(listener);
928         if (client == null) {
929             Log.e(TAG, "unregisterEnrollmentCallback(): listener was not previously "
930                     + "registered");
931             return;
932         }
933         listener.asBinder().unlinkToDeath(client, 0);
934         mEnrollmentStateClients.remove(client);
935     }
936 
937     /**
938      * Registers a {@link ICarTrustAgentBleCallback} to be notified for changes to the BLE state
939      * changes.
940      *
941      * @param listener {@link ICarTrustAgentBleCallback}
942      */
943     @Override
944     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
registerBleCallback(ICarTrustAgentBleCallback listener)945     public synchronized void registerBleCallback(ICarTrustAgentBleCallback listener) {
946         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
947         if (listener == null) {
948             throw new IllegalArgumentException("Listener is null");
949         }
950         // If a new client is registering, create a new EnrollmentStateClient and add it to the list
951         // of listening clients.
952         BleStateChangeClient client = findBleStateClientLocked(listener);
953         if (client == null) {
954             client = new BleStateChangeClient(listener);
955             try {
956                 listener.asBinder().linkToDeath(client, 0);
957             } catch (RemoteException e) {
958                 Log.e(TAG, "Cannot link death recipient to binder " + e);
959                 return;
960             }
961             mBleStateChangeClients.add(client);
962         }
963     }
964 
965     /**
966      * Iterates through the list of registered BLE State Change clients -
967      * {@link BleStateChangeClient} and finds if the given client is already registered.
968      *
969      * @param listener Listener to look for.
970      * @return the {@link BleStateChangeClient} if found, null if not
971      */
972     @Nullable
findBleStateClientLocked( ICarTrustAgentBleCallback listener)973     private BleStateChangeClient findBleStateClientLocked(
974             ICarTrustAgentBleCallback listener) {
975         IBinder binder = listener.asBinder();
976         // Find the listener by comparing the binder object they host.
977         for (BleStateChangeClient client : mBleStateChangeClients) {
978             if (client.isHoldingBinder(binder)) {
979                 return client;
980             }
981         }
982         return null;
983     }
984 
985     /**
986      * Unregister the given BLE State Change listener
987      *
988      * @param listener client to unregister
989      */
990     @Override
991     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
unregisterBleCallback(ICarTrustAgentBleCallback listener)992     public synchronized void unregisterBleCallback(ICarTrustAgentBleCallback listener) {
993         ICarImpl.assertTrustAgentEnrollmentPermission(mContext);
994         if (listener == null) {
995             throw new IllegalArgumentException("Listener is null");
996         }
997 
998         BleStateChangeClient client = findBleStateClientLocked(listener);
999         if (client == null) {
1000             Log.e(TAG, "unregisterBleCallback(): listener was not previously "
1001                     + "registered");
1002             return;
1003         }
1004         listener.asBinder().unlinkToDeath(client, 0);
1005         mBleStateChangeClients.remove(client);
1006     }
1007 
1008     /**
1009      * The interface that an enrollment delegate has to implement to add/remove escrow tokens.
1010      */
1011     interface CarTrustAgentEnrollmentRequestDelegate {
1012         /**
1013          * Add the given escrow token that was generated by the peer device that is being enrolled.
1014          *
1015          * @param token the 64 bit token
1016          * @param uid   user id
1017          */
addEscrowToken(byte[] token, int uid)1018         void addEscrowToken(byte[] token, int uid);
1019 
1020         /**
1021          * Remove the given escrow token.  This should be called when removing a trusted device.
1022          *
1023          * @param handle the 64 bit token
1024          * @param uid    user id
1025          */
removeEscrowToken(long handle, int uid)1026         void removeEscrowToken(long handle, int uid);
1027 
1028         /**
1029          * Query if the token is active.  The result is asynchronously delivered through a callback
1030          * {@link CarTrustAgentEnrollmentService#onEscrowTokenActiveStateChanged(long, boolean,
1031          * int)}
1032          *
1033          * @param handle the 64 bit token
1034          * @param uid    user id
1035          */
isEscrowTokenActive(long handle, int uid)1036         void isEscrowTokenActive(long handle, int uid);
1037     }
1038 
setEnrollmentRequestDelegate(CarTrustAgentEnrollmentRequestDelegate delegate)1039     void setEnrollmentRequestDelegate(CarTrustAgentEnrollmentRequestDelegate delegate) {
1040         mEnrollmentDelegate = delegate;
1041     }
1042 
dump(PrintWriter writer)1043     void dump(PrintWriter writer) {
1044         writer.println("*CarTrustAgentEnrollmentService*");
1045         writer.println("Enrollment Service Logs:");
1046         for (String log : mLogQueue) {
1047             writer.println("\t" + log);
1048         }
1049     }
1050 
addEnrollmentServiceLog(String message)1051     private void addEnrollmentServiceLog(String message) {
1052         if (mLogQueue.size() >= MAX_LOG_SIZE) {
1053             mLogQueue.remove();
1054         }
1055         mLogQueue.add(System.currentTimeMillis() + " : " + message);
1056     }
1057 
dispatchEscrowTokenActiveStateChanged(long handle, boolean active)1058     private void dispatchEscrowTokenActiveStateChanged(long handle, boolean active) {
1059         for (EnrollmentStateClient client : mEnrollmentStateClients) {
1060             try {
1061                 client.mListener.onEscrowTokenActiveStateChanged(handle, active);
1062             } catch (RemoteException e) {
1063                 Log.e(TAG, "Cannot notify client of a Token Activation change: " + active);
1064             }
1065         }
1066     }
1067 
dispatchEnrollmentFailure(int error)1068     private void dispatchEnrollmentFailure(int error) {
1069         for (EnrollmentStateClient client : mEnrollmentStateClients) {
1070             try {
1071                 client.mListener.onEnrollmentHandshakeFailure(null, error);
1072             } catch (RemoteException e) {
1073                 Log.e(TAG, "onEnrollmentHandshakeFailure dispatch failed", e);
1074             }
1075         }
1076     }
1077 
1078     /**
1079      * Currently, we store a map of uid -> a set of deviceId+deviceInfo strings
1080      * This method extracts deviceInfo from a device+deviceInfo string, which should be
1081      * created by {@link #serializeDeviceInfoWithId(TrustedDeviceInfo, String)}
1082      *
1083      * @param deviceInfoWithId deviceId+deviceInfo string
1084      */
1085     @Nullable
extractDeviceInfo(String deviceInfoWithId)1086     private static TrustedDeviceInfo extractDeviceInfo(String deviceInfoWithId) {
1087         int delimiterIndex = deviceInfoWithId.indexOf(DEVICE_INFO_DELIMITER);
1088         if (delimiterIndex < 0) {
1089             return null;
1090         }
1091         return TrustedDeviceInfo.deserialize(deviceInfoWithId.substring(delimiterIndex + 1));
1092     }
1093 
1094     /**
1095      * Extract deviceId from a deviceId+deviceInfo string which should be created by
1096      * {@link #serializeDeviceInfoWithId(TrustedDeviceInfo, String)}
1097      *
1098      * @param deviceInfoWithId deviceId+deviceInfo string
1099      */
1100     @Nullable
extractDeviceId(String deviceInfoWithId)1101     private static String extractDeviceId(String deviceInfoWithId) {
1102         int delimiterIndex = deviceInfoWithId.indexOf(DEVICE_INFO_DELIMITER);
1103         if (delimiterIndex < 0) {
1104             return null;
1105         }
1106         return deviceInfoWithId.substring(0, delimiterIndex);
1107     }
1108 
1109     // Create deviceId+deviceInfo string
serializeDeviceInfoWithId(TrustedDeviceInfo info, String id)1110     private static String serializeDeviceInfoWithId(TrustedDeviceInfo info, String id) {
1111         return new StringBuilder()
1112                 .append(id)
1113                 .append(DEVICE_INFO_DELIMITER)
1114                 .append(info.serialize())
1115                 .toString();
1116     }
1117 
1118     /**
1119      * Class that holds onto client related information - listener interface, process that hosts the
1120      * binder object etc.
1121      * <p>
1122      * It also registers for death notifications of the host.
1123      */
1124     private class EnrollmentStateClient implements DeathRecipient {
1125         private final IBinder mListenerBinder;
1126         private final ICarTrustAgentEnrollmentCallback mListener;
1127 
EnrollmentStateClient(ICarTrustAgentEnrollmentCallback listener)1128         EnrollmentStateClient(ICarTrustAgentEnrollmentCallback listener) {
1129             mListener = listener;
1130             mListenerBinder = listener.asBinder();
1131         }
1132 
1133         @Override
binderDied()1134         public void binderDied() {
1135             if (Log.isLoggable(TAG, Log.DEBUG)) {
1136                 Log.d(TAG, "Binder died " + mListenerBinder);
1137             }
1138             mListenerBinder.unlinkToDeath(this, 0);
1139             synchronized (CarTrustAgentEnrollmentService.this) {
1140                 mEnrollmentStateClients.remove(this);
1141             }
1142         }
1143 
1144         /**
1145          * Returns if the given binder object matches to what this client info holds.
1146          * Used to check if the listener asking to be registered is already registered.
1147          *
1148          * @return true if matches, false if not
1149          */
isHoldingBinder(IBinder binder)1150         public boolean isHoldingBinder(IBinder binder) {
1151             return mListenerBinder == binder;
1152         }
1153     }
1154 
1155     private class BleStateChangeClient implements DeathRecipient {
1156         private final IBinder mListenerBinder;
1157         private final ICarTrustAgentBleCallback mListener;
1158 
BleStateChangeClient(ICarTrustAgentBleCallback listener)1159         BleStateChangeClient(ICarTrustAgentBleCallback listener) {
1160             mListener = listener;
1161             mListenerBinder = listener.asBinder();
1162         }
1163 
1164         @Override
binderDied()1165         public void binderDied() {
1166             if (Log.isLoggable(TAG, Log.DEBUG)) {
1167                 Log.d(TAG, "Binder died " + mListenerBinder);
1168             }
1169             mListenerBinder.unlinkToDeath(this, 0);
1170             synchronized (CarTrustAgentEnrollmentService.this) {
1171                 mBleStateChangeClients.remove(this);
1172             }
1173         }
1174 
1175         /**
1176          * Returns if the given binder object matches to what this client info holds.
1177          * Used to check if the listener asking to be registered is already registered.
1178          *
1179          * @return true if matches, false if not
1180          */
isHoldingBinder(IBinder binder)1181         public boolean isHoldingBinder(IBinder binder) {
1182             return mListenerBinder == binder;
1183         }
1184 
onEnrollmentAdvertisementStarted()1185         public void onEnrollmentAdvertisementStarted() {
1186             try {
1187                 mListener.onEnrollmentAdvertisingStarted();
1188             } catch (RemoteException e) {
1189                 Log.e(TAG, "onEnrollmentAdvertisementStarted() failed", e);
1190             }
1191         }
1192     }
1193 
1194     /**
1195      * Returns the name that should be used for the device during enrollment of a trusted device.
1196      *
1197      * <p>The returned name will be a combination of a prefix sysprop and randomized digits.
1198      */
getEnrollmentDeviceName()1199     private String getEnrollmentDeviceName() {
1200         if (mEnrollmentDeviceName == null) {
1201             String deviceNamePrefix = CarProperties.trusted_device_device_name_prefix().orElse("");
1202             deviceNamePrefix = deviceNamePrefix.substring(
1203                 0, Math.min(deviceNamePrefix.length(), DEVICE_NAME_PREFIX_LIMIT));
1204 
1205             int randomNameLength = DEVICE_NAME_LENGTH_LIMIT - deviceNamePrefix.length();
1206             String randomName = Utils.generateRandomNumberString(randomNameLength);
1207             mEnrollmentDeviceName = deviceNamePrefix + randomName;
1208         }
1209         return mEnrollmentDeviceName;
1210     }
1211 }
1212