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