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