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