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.companiondevicesupport.feature.trust; 18 19 import static com.android.car.connecteddevice.util.SafeLog.logd; 20 import static com.android.car.connecteddevice.util.SafeLog.loge; 21 import static com.android.car.connecteddevice.util.SafeLog.logw; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.app.ActivityManager; 26 import android.app.Notification; 27 import android.app.NotificationChannel; 28 import android.app.NotificationManager; 29 import android.app.PendingIntent; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 36 import androidx.core.content.ContextCompat; 37 import androidx.room.Room; 38 39 import com.android.car.companiondevicesupport.R; 40 import com.android.car.companiondevicesupport.api.external.AssociatedDevice; 41 import com.android.car.companiondevicesupport.api.external.CompanionDevice; 42 import com.android.car.companiondevicesupport.api.external.IConnectedDeviceManager; 43 import com.android.car.companiondevicesupport.api.external.IDeviceAssociationCallback; 44 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceEnrollmentCallback; 45 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceAgentDelegate; 46 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceCallback; 47 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceManager; 48 import com.android.car.companiondevicesupport.api.internal.trust.TrustedDevice; 49 import com.android.car.companiondevicesupport.feature.trust.storage.TrustedDeviceDao; 50 import com.android.car.companiondevicesupport.feature.trust.storage.TrustedDeviceDatabase; 51 import com.android.car.companiondevicesupport.feature.trust.storage.TrustedDeviceEntity; 52 import com.android.car.companiondevicesupport.feature.trust.ui.TrustedDeviceActivity; 53 import com.android.car.companiondevicesupport.protos.PhoneAuthProto.PhoneCredentials; 54 import com.android.car.companiondevicesupport.protos.TrustedDeviceMessageProto.TrustedDeviceError; 55 import com.android.car.companiondevicesupport.protos.TrustedDeviceMessageProto.TrustedDeviceError.ErrorType; 56 import com.android.car.companiondevicesupport.protos.TrustedDeviceMessageProto.TrustedDeviceMessage.MessageType; 57 import com.android.car.companiondevicesupport.protos.TrustedDeviceMessageProto.TrustedDeviceMessage; 58 import com.android.car.connecteddevice.util.ByteUtils; 59 import com.android.car.connecteddevice.util.RemoteCallbackBinder; 60 import com.android.car.protobuf.ByteString; 61 import com.android.car.protobuf.InvalidProtocolBufferException; 62 63 import java.util.ArrayList; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 import java.util.concurrent.ConcurrentHashMap; 68 import java.util.concurrent.CopyOnWriteArraySet; 69 import java.util.concurrent.Executor; 70 import java.util.concurrent.Executors; 71 import java.util.concurrent.atomic.AtomicBoolean; 72 import java.util.function.Consumer; 73 74 75 /** Manager for the feature of unlocking the head unit with a user's trusted device. */ 76 public class TrustedDeviceManager extends ITrustedDeviceManager.Stub { 77 private static final String TAG = "TrustedDeviceManager"; 78 79 private static final String CHANNEL_ID = "trusteddevice_notification_channel"; 80 81 private static final int ENROLLMENT_NOTIFICATION_ID = 0; 82 83 private static final int TRUSTED_DEVICE_MESSAGE_VERSION = 2; 84 85 /** Length of token generated on a trusted device. */ 86 private static final int ESCROW_TOKEN_LENGTH = 8; 87 88 private final Map<IBinder, ITrustedDeviceCallback> mTrustedDeviceCallbacks = 89 new ConcurrentHashMap<>(); 90 91 private final Map<IBinder, ITrustedDeviceEnrollmentCallback> mEnrollmentCallbacks 92 = new ConcurrentHashMap<>(); 93 94 private final Map<IBinder, IDeviceAssociationCallback> mAssociatedDeviceCallbacks = 95 new ConcurrentHashMap<>(); 96 97 private final Set<RemoteCallbackBinder> mCallbackBinders = new CopyOnWriteArraySet<>(); 98 99 private final Context mContext; 100 101 private final TrustedDeviceFeature mTrustedDeviceFeature; 102 103 private final Executor mExecutor = Executors.newSingleThreadExecutor(); 104 105 private final AtomicBoolean mIsWaitingForCredentials = new AtomicBoolean(false); 106 107 private final NotificationManager mNotificationManager; 108 109 private TrustedDeviceDao mDatabase; 110 111 private ITrustedDeviceAgentDelegate mTrustAgentDelegate; 112 113 private CompanionDevice mPendingDevice; 114 115 private byte[] mPendingToken; 116 117 private PendingCredentials mPendingCredentials; 118 TrustedDeviceManager(@onNull Context context)119 TrustedDeviceManager(@NonNull Context context) { 120 mContext = context; 121 mTrustedDeviceFeature = new TrustedDeviceFeature(context); 122 mTrustedDeviceFeature.setCallback(mFeatureCallback); 123 mTrustedDeviceFeature.setAssociatedDeviceCallback(mAssociatedDeviceCallback); 124 mTrustedDeviceFeature.start(); 125 mDatabase = Room.databaseBuilder(context, TrustedDeviceDatabase.class, 126 TrustedDeviceDatabase.DATABASE_NAME).build().trustedDeviceDao(); 127 mNotificationManager = (NotificationManager) mContext. 128 getSystemService(Context.NOTIFICATION_SERVICE); 129 String channelName = mContext.getString(R.string.trusted_device_notification_channel_name); 130 NotificationChannel channel = new NotificationChannel(CHANNEL_ID, channelName, 131 NotificationManager.IMPORTANCE_HIGH); 132 mNotificationManager.createNotificationChannel(channel); 133 logd(TAG, "TrustedDeviceManager created successfully."); 134 } 135 cleanup()136 void cleanup() { 137 mPendingToken = null; 138 mPendingDevice = null; 139 mPendingCredentials = null; 140 mIsWaitingForCredentials.set(false); 141 mTrustedDeviceCallbacks.clear(); 142 mEnrollmentCallbacks.clear(); 143 mAssociatedDeviceCallbacks.clear(); 144 mTrustedDeviceFeature.stop(); 145 } 146 startEnrollment(@onNull CompanionDevice device, @NonNull byte[] token)147 private void startEnrollment(@NonNull CompanionDevice device, @NonNull byte[] token) { 148 logd(TAG, "Starting trusted device enrollment process."); 149 mPendingDevice = device; 150 showEnrollmentNotification(); 151 152 mPendingToken = token; 153 if (mTrustAgentDelegate == null) { 154 logd(TAG, "No trust agent delegate has been set yet. No further enrollment action " 155 + "can be taken at this time."); 156 return; 157 } 158 159 try { 160 mTrustAgentDelegate.addEscrowToken(token, ActivityManager.getCurrentUser()); 161 } catch (RemoteException e) { 162 loge(TAG, "Error while adding token through delegate.", e); 163 } 164 } 165 showEnrollmentNotification()166 private void showEnrollmentNotification() { 167 UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser()); 168 Intent enrollmentIntent = new Intent(mContext, TrustedDeviceActivity.class) 169 // Setting this action ensures that the TrustedDeviceActivity is resumed if it is 170 // already running. 171 .setAction("com.android.settings.action.EXTRA_SETTINGS") 172 .putExtra(TrustedDeviceConstants.INTENT_EXTRA_ENROLL_NEW_TOKEN, true) 173 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 174 PendingIntent pendingIntent = PendingIntent 175 .getActivityAsUser(mContext, /* requestCode = */ 0, enrollmentIntent, 176 PendingIntent.FLAG_UPDATE_CURRENT, null, currentUser); 177 Notification notification = new Notification.Builder(mContext, CHANNEL_ID) 178 .setSmallIcon(R.drawable.ic_directions_car_filled) 179 .setColor(ContextCompat.getColor(mContext, R.color.car_red_300)) 180 .setContentTitle(mContext.getString(R.string.trusted_device_notification_title)) 181 .setContentText(mContext.getString(R.string.trusted_device_notification_content)) 182 .setContentIntent(pendingIntent) 183 .setAutoCancel(true) 184 .build(); 185 mNotificationManager.notifyAsUser(/* tag = */ null, ENROLLMENT_NOTIFICATION_ID, 186 notification, currentUser); 187 } 188 unlockUser(@onNull String deviceId, @NonNull PhoneCredentials credentials)189 private void unlockUser(@NonNull String deviceId, @NonNull PhoneCredentials credentials) { 190 logd(TAG, "Unlocking with credentials."); 191 try { 192 mTrustAgentDelegate.unlockUserWithToken(credentials.getEscrowToken().toByteArray(), 193 ByteUtils.bytesToLong(credentials.getHandle().toByteArray()), 194 ActivityManager.getCurrentUser()); 195 mTrustedDeviceFeature.sendMessageSecurely(deviceId, createAcknowledgmentMessage()); 196 } catch (RemoteException e) { 197 loge(TAG, "Error while unlocking user through delegate.", e); 198 } 199 } 200 201 @Override onEscrowTokenAdded(int userId, long handle)202 public void onEscrowTokenAdded(int userId, long handle) { 203 logd(TAG, "Escrow token has been successfully added."); 204 mPendingToken = null; 205 206 if (mEnrollmentCallbacks.size() == 0) { 207 mIsWaitingForCredentials.set(true); 208 return; 209 } 210 211 mIsWaitingForCredentials.set(false); 212 notifyEnrollmentCallbacks(callback -> { 213 try { 214 callback.onValidateCredentialsRequest(); 215 } catch (RemoteException e) { 216 loge(TAG, "Error while requesting credential validation.", e); 217 } 218 }); 219 } 220 221 @Override onEscrowTokenActivated(int userId, long handle)222 public void onEscrowTokenActivated(int userId, long handle) { 223 if (mPendingDevice == null) { 224 loge(TAG, "Unable to complete device enrollment. Pending device was null."); 225 return; 226 } 227 228 logd(TAG, "Enrollment completed successfully! Sending handle to connected device and " 229 + "persisting trusted device record."); 230 231 mTrustedDeviceFeature.sendMessageSecurely(mPendingDevice, createHandleMessage(handle)); 232 233 String deviceId = mPendingDevice.getDeviceId(); 234 235 TrustedDeviceEntity entity = new TrustedDeviceEntity(); 236 entity.id = deviceId; 237 entity.userId = userId; 238 entity.handle = handle; 239 mDatabase.addOrReplaceTrustedDevice(entity); 240 241 mPendingDevice = null; 242 243 TrustedDevice trustedDevice = new TrustedDevice(deviceId, userId, handle); 244 notifyTrustedDeviceCallbacks(callback -> { 245 try { 246 callback.onTrustedDeviceAdded(trustedDevice); 247 } catch (RemoteException e) { 248 loge(TAG, "Failed to notify that enrollment completed successfully.", e); 249 } 250 }); 251 } 252 253 @Override getTrustedDevicesForActiveUser()254 public List<TrustedDevice> getTrustedDevicesForActiveUser() { 255 List<TrustedDeviceEntity> foundEntities = 256 mDatabase.getTrustedDevicesForUser(ActivityManager.getCurrentUser()); 257 258 List<TrustedDevice> trustedDevices = new ArrayList<>(); 259 if (foundEntities == null) { 260 return trustedDevices; 261 } 262 263 for (TrustedDeviceEntity entity : foundEntities) { 264 trustedDevices.add(entity.toTrustedDevice()); 265 } 266 267 return trustedDevices; 268 } 269 270 @Override removeTrustedDevice(TrustedDevice trustedDevice)271 public void removeTrustedDevice(TrustedDevice trustedDevice) { 272 if (mTrustAgentDelegate == null) { 273 loge(TAG, "No TrustAgent delegate has been set. Unable to remove trusted device."); 274 return; 275 } 276 277 try { 278 mTrustAgentDelegate.removeEscrowToken(trustedDevice.getHandle(), 279 trustedDevice.getUserId()); 280 mDatabase.removeTrustedDevice(new TrustedDeviceEntity(trustedDevice)); 281 } catch (RemoteException e) { 282 loge(TAG, "Error while removing token through delegate.", e); 283 return; 284 } 285 notifyTrustedDeviceCallbacks(callback -> { 286 try { 287 callback.onTrustedDeviceRemoved(trustedDevice); 288 } catch (RemoteException e) { 289 loge(TAG, "Failed to notify that a trusted device has been removed.", e); 290 } 291 }); 292 } 293 294 @Override getActiveUserConnectedDevices()295 public List<CompanionDevice> getActiveUserConnectedDevices() { 296 List<CompanionDevice> devices = new ArrayList<>(); 297 IConnectedDeviceManager manager = mTrustedDeviceFeature.getConnectedDeviceManager(); 298 if (manager == null) { 299 loge(TAG, "Unable to get connected devices. Service not connected. "); 300 return devices; 301 } 302 try { 303 devices = manager.getActiveUserConnectedDevices(); 304 } catch (RemoteException e) { 305 loge(TAG, "Failed to get connected devices. ", e); 306 } 307 return devices; 308 } 309 310 @Override initiateEnrollment(String deviceId)311 public void initiateEnrollment(String deviceId) { 312 mTrustedDeviceFeature.sendMessageSecurely(deviceId, createStartEnrollmentMessage()); 313 } 314 315 @Override registerTrustedDeviceCallback(ITrustedDeviceCallback callback)316 public void registerTrustedDeviceCallback(ITrustedDeviceCallback callback) { 317 mTrustedDeviceCallbacks.put(callback.asBinder(), callback); 318 RemoteCallbackBinder remoteBinder = new RemoteCallbackBinder(callback.asBinder(), iBinder -> 319 unregisterTrustedDeviceCallback(callback)); 320 mCallbackBinders.add(remoteBinder); 321 } 322 323 @Override unregisterTrustedDeviceCallback(ITrustedDeviceCallback callback)324 public void unregisterTrustedDeviceCallback(ITrustedDeviceCallback callback) { 325 IBinder binder = callback.asBinder(); 326 mTrustedDeviceCallbacks.remove(binder); 327 removeRemoteBinder(binder); 328 } 329 330 @Override registerAssociatedDeviceCallback(IDeviceAssociationCallback callback)331 public void registerAssociatedDeviceCallback(IDeviceAssociationCallback callback) { 332 mAssociatedDeviceCallbacks.put(callback.asBinder(), callback); 333 RemoteCallbackBinder remoteBinder = new RemoteCallbackBinder(callback.asBinder(), iBinder -> 334 unregisterAssociatedDeviceCallback(callback)); 335 mCallbackBinders.add(remoteBinder); 336 } 337 338 @Override unregisterAssociatedDeviceCallback(IDeviceAssociationCallback callback)339 public void unregisterAssociatedDeviceCallback(IDeviceAssociationCallback callback) { 340 IBinder binder = callback.asBinder(); 341 mAssociatedDeviceCallbacks.remove(binder); 342 removeRemoteBinder(binder); 343 } 344 345 @Override registerTrustedDeviceEnrollmentCallback( ITrustedDeviceEnrollmentCallback callback)346 public void registerTrustedDeviceEnrollmentCallback( 347 ITrustedDeviceEnrollmentCallback callback) { 348 mEnrollmentCallbacks.put(callback.asBinder(), callback); 349 RemoteCallbackBinder remoteBinder = new RemoteCallbackBinder(callback.asBinder(), 350 iBinder -> unregisterTrustedDeviceEnrollmentCallback(callback)); 351 mCallbackBinders.add(remoteBinder); 352 // A token has been added and is waiting on user credential validation. 353 if (mIsWaitingForCredentials.getAndSet(false)) { 354 mExecutor.execute(() -> { 355 try { 356 callback.onValidateCredentialsRequest(); 357 } catch (RemoteException e) { 358 loge(TAG, "Error while notifying enrollment listener.", e); 359 } 360 }); 361 } 362 } 363 364 @Override unregisterTrustedDeviceEnrollmentCallback( ITrustedDeviceEnrollmentCallback callback)365 public void unregisterTrustedDeviceEnrollmentCallback( 366 ITrustedDeviceEnrollmentCallback callback) { 367 IBinder binder = callback.asBinder(); 368 mEnrollmentCallbacks.remove(binder); 369 removeRemoteBinder(binder); 370 } 371 372 @Override setTrustedDeviceAgentDelegate(ITrustedDeviceAgentDelegate trustAgentDelegate)373 public void setTrustedDeviceAgentDelegate(ITrustedDeviceAgentDelegate trustAgentDelegate) { 374 logd(TAG, "Set trusted device agent delegate: " + trustAgentDelegate + "."); 375 mTrustAgentDelegate = trustAgentDelegate; 376 377 // Add pending token if present. 378 if (mPendingToken != null) { 379 try { 380 trustAgentDelegate.addEscrowToken(mPendingToken, ActivityManager.getCurrentUser()); 381 } catch (RemoteException e) { 382 loge(TAG, "Error while adding token through delegate.", e); 383 } 384 return; 385 } 386 387 // Unlock with pending credentials if present. 388 if (mPendingCredentials != null) { 389 unlockUser(mPendingCredentials.mDeviceId, mPendingCredentials.mPhoneCredentials); 390 mPendingCredentials = null; 391 } 392 } 393 394 @Override clearTrustedDeviceAgentDelegate(ITrustedDeviceAgentDelegate trustAgentDelegate)395 public void clearTrustedDeviceAgentDelegate(ITrustedDeviceAgentDelegate trustAgentDelegate) { 396 if (trustAgentDelegate.asBinder() != mTrustAgentDelegate.asBinder()) { 397 logd(TAG, "TrustedDeviceAgentDelegate " + trustAgentDelegate + " doesn't match the " + 398 "current TrustedDeviceAgentDelegate: " + mTrustAgentDelegate + 399 ". Ignoring call to clear."); 400 return; 401 } 402 logd(TAG, "Clear current TrustedDeviceAgentDelegate: " + trustAgentDelegate + "."); 403 mTrustAgentDelegate = null; 404 } 405 areCredentialsValid(@ullable PhoneCredentials credentials)406 private boolean areCredentialsValid(@Nullable PhoneCredentials credentials) { 407 return credentials != null && credentials.getEscrowToken() != null 408 && credentials.getHandle() != null; 409 } 410 processEnrollmentMessage(@onNull CompanionDevice device, @Nullable ByteString payload)411 private void processEnrollmentMessage(@NonNull CompanionDevice device, 412 @Nullable ByteString payload) { 413 if (payload == null) { 414 logw(TAG, "Received enrollment message with null payload. Ignoring."); 415 return; 416 } 417 418 byte[] message = payload.toByteArray(); 419 if (message.length != ESCROW_TOKEN_LENGTH) { 420 logw(TAG, "Received invalid escrow token of length " + message.length + ". Ignoring."); 421 return; 422 } 423 424 startEnrollment(device, message); 425 } 426 processUnlockMessage(@onNull CompanionDevice device, @Nullable ByteString payload)427 private void processUnlockMessage(@NonNull CompanionDevice device, 428 @Nullable ByteString payload) { 429 if (payload == null) { 430 logw(TAG, "Received unlock message with null payload. Ignoring."); 431 return; 432 } 433 byte[] message = payload.toByteArray(); 434 435 PhoneCredentials credentials = null; 436 try { 437 credentials = PhoneCredentials.parseFrom(message); 438 } catch (InvalidProtocolBufferException e) { 439 loge(TAG, "Unable to parse credentials from device. Not unlocking head unit."); 440 return; 441 } 442 443 if (!areCredentialsValid(credentials)) { 444 loge(TAG, "Received invalid credentials from device. Not unlocking head unit."); 445 return; 446 } 447 448 TrustedDeviceEntity entity = mDatabase.getTrustedDevice(device.getDeviceId()); 449 450 if (entity == null) { 451 logw(TAG, "Received unlock request from an untrusted device."); 452 // TODO(b/145618412) Notify device that it is no longer trusted. 453 return; 454 } 455 456 if (entity.userId != ActivityManager.getCurrentUser()) { 457 logw(TAG, "Received credentials from background user " + entity.userId 458 + ". Ignoring."); 459 return; 460 } 461 462 TrustedDeviceEventLog.onCredentialsReceived(); 463 464 if (mTrustAgentDelegate == null) { 465 logd(TAG, "No trust agent delegate set yet. Credentials will be delivered once " 466 + "set."); 467 mPendingCredentials = new PendingCredentials(device.getDeviceId(), credentials); 468 return; 469 } 470 471 logd(TAG, "Received unlock credentials from trusted device " + device.getDeviceId() 472 + ". Attempting unlock."); 473 474 unlockUser(device.getDeviceId(), credentials); 475 } 476 processErrorMessage(@ullable ByteString payload)477 private void processErrorMessage(@Nullable ByteString payload) { 478 if (payload == null) { 479 logw(TAG, "Received error message with null payload. Ignoring."); 480 return; 481 } 482 TrustedDeviceError trustedDeviceError = null; 483 try { 484 trustedDeviceError = TrustedDeviceError.parseFrom(payload.toByteArray()); 485 } catch (InvalidProtocolBufferException e) { 486 loge(TAG, "Received error message from client, but cannot parse.", e); 487 notifyEnrollmentError(TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_UNKNOWN); 488 return; 489 } 490 int errorType = trustedDeviceError.getTypeValue(); 491 int error; 492 switch (errorType) { 493 case ErrorType.MESSAGE_TYPE_UNKNOWN_VALUE: 494 error = TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_MESSAGE_TYPE_UNKNOWN; 495 break; 496 case ErrorType.DEVICE_NOT_SECURED_VALUE: 497 error = TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_DEVICE_NOT_SECURED; 498 break; 499 default: 500 loge(TAG, "Encountered unexpected error type: " + errorType + "."); 501 error = TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_UNKNOWN; 502 } 503 notifyEnrollmentError(error); 504 } 505 notifyEnrollmentError(@rustedDeviceConstants.TrustedDeviceError int error)506 private void notifyEnrollmentError(@TrustedDeviceConstants.TrustedDeviceError int error) { 507 notifyEnrollmentCallbacks(callback -> { 508 try { 509 callback.onTrustedDeviceEnrollmentError(error); 510 } catch (RemoteException e) { 511 loge(TAG, "Error while notifying enrollment error.", e); 512 } 513 }); 514 } 515 notifyTrustedDeviceCallbacks(Consumer<ITrustedDeviceCallback> notification)516 private void notifyTrustedDeviceCallbacks(Consumer<ITrustedDeviceCallback> notification) { 517 mTrustedDeviceCallbacks.forEach((iBinder, callback) -> mExecutor.execute(() -> 518 notification.accept(callback))); 519 } 520 notifyEnrollmentCallbacks( Consumer<ITrustedDeviceEnrollmentCallback> notification )521 private void notifyEnrollmentCallbacks( 522 Consumer<ITrustedDeviceEnrollmentCallback> notification ) { 523 mEnrollmentCallbacks.forEach((iBinder, callback) -> mExecutor.execute(() -> 524 notification.accept(callback))); 525 } 526 notifyAssociatedDeviceCallbacks( Consumer<IDeviceAssociationCallback> notification )527 private void notifyAssociatedDeviceCallbacks( 528 Consumer<IDeviceAssociationCallback> notification ) { 529 mAssociatedDeviceCallbacks.forEach((iBinder, callback) -> mExecutor.execute(() -> 530 notification.accept(callback))); 531 } 532 removeRemoteBinder(IBinder binder)533 private void removeRemoteBinder(IBinder binder) { 534 RemoteCallbackBinder remoteBinderToRemove = null; 535 for (RemoteCallbackBinder remoteBinder : mCallbackBinders) { 536 if (remoteBinder.getCallbackBinder().equals(binder)) { 537 remoteBinderToRemove = remoteBinder; 538 break; 539 } 540 } 541 if (remoteBinderToRemove != null) { 542 remoteBinderToRemove.cleanUp(); 543 mCallbackBinders.remove(remoteBinderToRemove); 544 } 545 } 546 createAcknowledgmentMessage()547 private byte[] createAcknowledgmentMessage() { 548 return TrustedDeviceMessage.newBuilder() 549 .setVersion(TRUSTED_DEVICE_MESSAGE_VERSION) 550 .setType(MessageType.ACK) 551 .build() 552 .toByteArray(); 553 } 554 createHandleMessage(long handle)555 private byte[] createHandleMessage(long handle) { 556 return TrustedDeviceMessage.newBuilder() 557 .setVersion(TRUSTED_DEVICE_MESSAGE_VERSION) 558 .setType(MessageType.HANDLE) 559 .setPayload(ByteString.copyFrom(ByteUtils.longToBytes(handle))) 560 .build() 561 .toByteArray(); 562 } 563 createStartEnrollmentMessage()564 private byte[] createStartEnrollmentMessage() { 565 return TrustedDeviceMessage.newBuilder() 566 .setVersion(TRUSTED_DEVICE_MESSAGE_VERSION) 567 .setType(MessageType.START_ENROLLMENT) 568 .build() 569 .toByteArray(); 570 } 571 572 private final TrustedDeviceFeature.Callback mFeatureCallback = 573 new TrustedDeviceFeature.Callback() { 574 @Override 575 public void onMessageReceived(CompanionDevice device, byte[] message) { 576 TrustedDeviceMessage trustedDeviceMessage = null; 577 try { 578 trustedDeviceMessage = TrustedDeviceMessage.parseFrom(message); 579 } catch (InvalidProtocolBufferException e) { 580 loge(TAG, "Received message from client, but cannot parse.", e); 581 return; 582 } 583 584 switch (trustedDeviceMessage.getType()) { 585 case ESCROW_TOKEN: 586 processEnrollmentMessage(device, trustedDeviceMessage.getPayload()); 587 break; 588 case UNLOCK_CREDENTIALS: 589 processUnlockMessage(device, trustedDeviceMessage.getPayload()); 590 break; 591 case ACK: 592 // The client sends an acknowledgment when the handle has been received, but 593 // nothing needs to be on the IHU side. So simply log this message. 594 logd(TAG, "Received acknowledgment message from client."); 595 break; 596 case ERROR: 597 processErrorMessage(trustedDeviceMessage.getPayload()); 598 break; 599 default: 600 // The client should only be sending requests to either enroll or unlock. 601 loge(TAG, "Received a message from the client with an invalid MessageType ( " 602 + trustedDeviceMessage.getType() + "). Ignoring."); 603 } 604 } 605 606 @Override 607 public void onDeviceError(CompanionDevice device, int error) { 608 } 609 }; 610 611 private final TrustedDeviceFeature.AssociatedDeviceCallback mAssociatedDeviceCallback = 612 new TrustedDeviceFeature.AssociatedDeviceCallback() { 613 @Override 614 public void onAssociatedDeviceAdded(AssociatedDevice device) { 615 notifyAssociatedDeviceCallbacks(callback -> { 616 try { 617 callback.onAssociatedDeviceAdded(device); 618 } catch (RemoteException e) { 619 loge(TAG, "Failed to notify that an associated device has been added.", e); 620 } 621 }); 622 } 623 624 @Override 625 public void onAssociatedDeviceRemoved(AssociatedDevice device) { 626 List<TrustedDevice> devices = getTrustedDevicesForActiveUser(); 627 if (devices == null || devices.isEmpty()) { 628 return; 629 } 630 TrustedDevice deviceToRemove = null; 631 for (TrustedDevice trustedDevice : devices) { 632 if (trustedDevice.getDeviceId().equals(device.getDeviceId())) { 633 deviceToRemove = trustedDevice; 634 break; 635 } 636 } 637 if (deviceToRemove != null) { 638 removeTrustedDevice(deviceToRemove); 639 } 640 notifyAssociatedDeviceCallbacks(callback -> { 641 try { 642 callback.onAssociatedDeviceRemoved(device); 643 } catch (RemoteException e) { 644 loge(TAG, "Failed to notify that an associated device has been " + 645 "removed.", e); 646 } 647 }); 648 } 649 650 @Override 651 public void onAssociatedDeviceUpdated(AssociatedDevice device) { 652 notifyAssociatedDeviceCallbacks(callback -> { 653 try { 654 callback.onAssociatedDeviceUpdated(device); 655 } catch (RemoteException e) { 656 loge(TAG, "Failed to notify that an associated device has been " + 657 "updated.", e); 658 } 659 }); 660 } 661 }; 662 663 private static class PendingCredentials { 664 final String mDeviceId; 665 final PhoneCredentials mPhoneCredentials; 666 PendingCredentials(@onNull String deviceId, @NonNull PhoneCredentials credentials)667 PendingCredentials(@NonNull String deviceId, @NonNull PhoneCredentials credentials) { 668 mDeviceId = deviceId; 669 mPhoneCredentials = credentials; 670 } 671 } 672 } 673