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