blob: c77600911f2cfebae42f6b4f24ad8804c9bbd5c8 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.connecteddevice.trust;
import static com.google.android.connecteddevice.util.SafeLog.logd;
import static com.google.android.connecteddevice.util.SafeLog.loge;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.KeyguardManager.WeakEscrowTokenRemovedListener;
import android.content.Context;
import android.os.RemoteException;
import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import com.google.android.connecteddevice.trust.api.ITrustedDeviceAgentDelegate;
import com.google.android.connecteddevice.trust.storage.TrustedDeviceDatabase;
import com.google.android.connecteddevice.trust.storage.TrustedDeviceDatabaseProvider;
import com.google.android.connecteddevice.trust.storage.TrustedDeviceEntity;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Manager for the feature of unlocking the head unit with a user's trusted device for T+.
*
* <p>This manager uses weak escrow token associated with the a user's trusted device to unlock the
* head unit.
*/
public class TrustedDeviceManagerApi33 extends TrustedDeviceManager {
private static final String TAG = "TrustedDeviceManagerApi33";
private final WeakEscrowTokenRemovedListener listener = this::onWeakEscrowTokenRemoved;
private final KeyguardManager keyguardManager;
TrustedDeviceManagerApi33(@NonNull Context context) {
this(
context.getSystemService(KeyguardManager.class),
TrustedDeviceDatabaseProvider.get(context),
new TrustedDeviceFeature(context),
/* databaseExecutor= */ Executors.newSingleThreadExecutor(),
/* remoteCallbackExecutor= */ Executors.newSingleThreadExecutor());
}
@VisibleForTesting
TrustedDeviceManagerApi33(
@NonNull KeyguardManager keyguardManager,
@NonNull TrustedDeviceDatabase database,
@NonNull TrustedDeviceFeature trustedDeviceFeature,
@NonNull Executor databaseExecutor,
@NonNull Executor remoteCallbackExecutor) {
super(database, trustedDeviceFeature, databaseExecutor, remoteCallbackExecutor);
this.keyguardManager = keyguardManager;
this.keyguardManager.registerWeakEscrowTokenRemovedListener(databaseExecutor, listener);
}
@Override
public void processEnrollment(boolean isDeviceSecure) {
byte[] token = getPendingToken();
if (token == null) {
loge(TAG, "No pending token can be added.");
return;
}
if (!isDeviceSecure) {
logd(TAG, "Processing enrollment on insecure device.");
addWeakEscrowToken(token, /* isDeviceSecure= */ false);
return;
}
logd(TAG, "Processing enrollment on secure device.");
if (isWaitingForCredentialSetUp.getAndSet(false)) {
if (pendingHandle == null) {
loge(TAG, "No pending handle can be added.");
return;
}
onWeakEscrowTokenActivated(
pendingHandle.userId, pendingHandle.handle, /* isDeviceSecure= */ true);
return;
}
addWeakEscrowToken(token, /* isDeviceSecure= */ true);
}
@Override
public void setTrustedDeviceAgentDelegate(ITrustedDeviceAgentDelegate trustAgentDelegate) {
setTrustedDeviceAgentDelegateInternal(trustAgentDelegate);
}
@Override
protected boolean removeEscrowToken(long handle, int userId) {
logd(TAG, "Removing weak token for user " + userId + ".");
return keyguardManager.removeWeakEscrowToken(handle, UserHandle.of(userId));
}
@Override
void cleanup() {
keyguardManager.unregisterWeakEscrowTokenRemovedListener(listener);
super.cleanup();
}
@WorkerThread
private void onWeakEscrowTokenRemoved(long handle, UserHandle user) {
int userId = user.getIdentifier();
logd(TAG, "A weak escrow token has been removed for user " + user + ".");
List<TrustedDeviceEntity> entities =
getTrustedDeviceDatabase().getValidTrustedDevicesForUser(userId);
for (TrustedDeviceEntity entity : entities) {
if (entity.handle == handle) {
invalidateTrustedDevice(entity);
getTrustedDeviceDatabase().removeTrustedDevice(entity);
break;
}
}
}
private void addWeakEscrowToken(@NonNull byte[] token, boolean isDeviceSecure) {
int userId = ActivityManager.getCurrentUser();
logd(TAG, "Adding weak escrow token for user " + userId + ".");
long addedHandle =
keyguardManager.addWeakEscrowToken(
token,
UserHandle.of(userId),
Runnable::run,
(handle, user) ->
onWeakEscrowTokenActivated(user.getIdentifier(), handle, isDeviceSecure));
pendingHandle = new PendingHandle(userId, addedHandle);
if (!isDeviceSecure) {
return;
}
notifyRemoteEnrollmentCallbacks(
callback -> {
try {
callback.onValidateCredentialsRequest();
} catch (RemoteException e) {
loge(TAG, "Error while requesting credential validation.", e);
}
});
}
private void onWeakEscrowTokenActivated(int userId, long handle, boolean isDeviceSecure) {
logd(TAG, "Weak Escrow token has been successfully added.");
if (isDeviceSecure) {
onEscrowTokenActivated(userId, handle);
return;
}
isWaitingForCredentialSetUp.set(true);
notifyRemoteEnrollmentCallbacks(
callback -> {
try {
callback.onSecureDeviceRequest();
} catch (RemoteException e) {
loge(TAG, "Error while requesting secured device.", e);
}
});
}
}