blob: 6feb5fa418bbce24da36f92a7e4e28b86e013c37 [file] [log] [blame]
/*
* Copyright (C) 2020 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.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.SynchronousUserSwitchObserver;
import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
* its extended minor versions.
*/
public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "Fingerprint21";
private static final int ENROLL_TIMEOUT_SEC = 60;
private boolean mTestHalEnabled;
final Context mContext;
@NonNull private final FingerprintStateCallback mFingerprintStateCallback;
private final ActivityTaskManager mActivityTaskManager;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
private final BiometricScheduler mScheduler;
private final Handler mHandler;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final LockoutFrameworkImpl mLockoutTracker;
private final BiometricTaskStackListener mTaskStackListener;
private final HalClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
private final Map<Integer, Long> mAuthenticatorIds;
@Nullable private IBiometricsFingerprint mDaemon;
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
@Nullable private ISidefpsController mSidefpsController;
// for requests that do not use biometric prompt
@NonNull private final AtomicLong mRequestCounter = new AtomicLong(0);
private int mCurrentUserId = UserHandle.USER_NULL;
private final boolean mIsUdfps;
private final int mSensorId;
private final boolean mIsPowerbuttonFps;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
public void onTaskStackChanged() {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof AuthenticationClient)) {
Slog.e(TAG, "Task stack changed for client: " + client);
return;
}
if (Utils.isKeyguard(mContext, client.getOwnerString())
|| Utils.isSystem(mContext, client.getOwnerString())) {
return; // Keyguard is always allowed
}
final List<ActivityManager.RunningTaskInfo> runningTasks =
mActivityTaskManager.getTasks(1);
if (!runningTasks.isEmpty()) {
final String topPackage = runningTasks.get(0).topActivity.getPackageName();
if (!topPackage.contentEquals(client.getOwnerString())
&& !client.isAlreadyDone()) {
Slog.e(TAG, "Stopping background authentication, top: "
+ topPackage + " currentClient: " + client);
mScheduler.cancelAuthenticationOrDetection(
client.getToken(), client.getRequestId());
}
}
});
}
}
private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback =
new LockoutFrameworkImpl.LockoutResetCallback() {
@Override
public void onLockoutReset(int userId) {
mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
}
};
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
public void onUserSwitching(int newUserId) {
scheduleInternalCleanup(newUserId, null /* callback */);
}
};
public static class HalResultController extends IBiometricsFingerprintClientCallback.Stub {
/**
* Interface to sends results to the HalResultController's owner.
*/
public interface Callback {
/**
* Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
*/
void onHardwareUnavailable();
}
private final int mSensorId;
@NonNull private final Context mContext;
@NonNull final Handler mHandler;
@NonNull final BiometricScheduler mScheduler;
@Nullable private Callback mCallback;
HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
@NonNull BiometricScheduler scheduler) {
mSensorId = sensorId;
mContext = context;
mHandler = handler;
mScheduler = scheduler;
}
public void setCallback(@Nullable Callback callback) {
mCallback = callback;
}
@Override
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintEnrollClient)) {
Slog.e(TAG, "onEnrollResult for non-enroll client: "
+ Utils.getClientName(client));
return;
}
final int currentUserId = client.getTargetUserId();
final CharSequence name = FingerprintUtils.getLegacyInstance(mSensorId)
.getUniqueName(mContext, currentUserId);
final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
enrollClient.onEnrollResult(fingerprint, remaining);
});
}
@Override
public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
}
@Override
public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof AcquisitionClient)) {
Slog.e(TAG, "onAcquired for non-acquisition client: "
+ Utils.getClientName(client));
return;
}
final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
acquisitionClient.onAcquired(acquiredInfo, vendorCode);
});
}
@Override
public void onAuthenticated(long deviceId, int fingerId, int groupId,
ArrayList<Byte> token) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof AuthenticationConsumer)) {
Slog.e(TAG, "onAuthenticated for non-authentication consumer: "
+ Utils.getClientName(client));
return;
}
final AuthenticationConsumer authenticationConsumer =
(AuthenticationConsumer) client;
final boolean authenticated = fingerId != 0;
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
authenticationConsumer.onAuthenticated(fp, authenticated, token);
});
}
@Override
public void onError(long deviceId, int error, int vendorCode) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
Slog.d(TAG, "handleError"
+ ", client: " + Utils.getClientName(client)
+ ", error: " + error
+ ", vendorCode: " + vendorCode);
if (!(client instanceof ErrorConsumer)) {
Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
return;
}
final ErrorConsumer errorConsumer = (ErrorConsumer) client;
errorConsumer.onError(error, vendorCode);
if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
if (mCallback != null) {
mCallback.onHardwareUnavailable();
}
}
});
}
@Override
public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.post(() -> {
Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining);
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof RemovalConsumer)) {
Slog.e(TAG, "onRemoved for non-removal consumer: "
+ Utils.getClientName(client));
return;
}
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
final RemovalConsumer removalConsumer = (RemovalConsumer) client;
removalConsumer.onRemoved(fp, remaining);
});
}
@Override
public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof EnumerateConsumer)) {
Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
+ Utils.getClientName(client));
return;
}
final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
enumerateConsumer.onEnumerationResult(fp, remaining);
});
}
}
Fingerprint21(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull BiometricScheduler scheduler,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
mFingerprintStateCallback = fingerprintStateCallback;
mSensorProperties = sensorProps;
mSensorId = sensorProps.sensorId;
mIsUdfps = sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
|| sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
mIsPowerbuttonFps = sensorProps.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON;
mScheduler = scheduler;
mHandler = handler;
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
mLazyDaemon = Fingerprint21.this::getDaemon;
mLockoutResetDispatcher = lockoutResetDispatcher;
mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
mHalResultController = controller;
mHalResultController.setCallback(() -> {
mDaemon = null;
mCurrentUserId = UserHandle.USER_NULL;
});
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to register user switch observer");
}
}
public static Fingerprint21 newInstance(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
final BiometricScheduler scheduler =
new BiometricScheduler(TAG,
BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(sensorProps.sensorId,
context, handler, scheduler);
return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
lockoutResetDispatcher, controller);
}
@Override
public void serviceDied(long cookie) {
Slog.e(TAG, "HAL died");
mHandler.post(() -> {
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId)
.incrementHALDeathCount();
mDaemon = null;
mCurrentUserId = UserHandle.USER_NULL;
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (client instanceof ErrorConsumer) {
Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
final ErrorConsumer errorConsumer = (ErrorConsumer) client;
errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ISSUE_HAL_DEATH,
-1 /* sensorId */);
}
mScheduler.recordCrashState();
mScheduler.reset();
});
}
@VisibleForTesting
synchronized IBiometricsFingerprint getDaemon() {
if (mTestHalEnabled) {
final TestHal testHal = new TestHal(mContext, mSensorId);
testHal.setNotify(mHalResultController);
return testHal;
}
if (mDaemon != null) {
return mDaemon;
}
Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ mScheduler.getCurrentClient());
try {
mDaemon = IBiometricsFingerprint.getService();
} catch (java.util.NoSuchElementException e) {
// Service doesn't exist or cannot be opened.
Slog.w(TAG, "NoSuchElementException", e);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to get fingerprint HAL", e);
}
if (mDaemon == null) {
Slog.w(TAG, "Fingerprint HAL not available");
return null;
}
mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
// HAL ID for these HIDL versions are only used to determine if callbacks have been
// successfully set.
long halId = 0;
try {
halId = mDaemon.setNotify(mHalResultController);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set callback for fingerprint HAL", e);
mDaemon = null;
}
Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
if (halId != 0) {
scheduleLoadAuthenticatorIds();
scheduleInternalCleanup(ActivityManager.getCurrentUser(), null /* callback */);
} else {
Slog.e(TAG, "Unable to set callback");
mDaemon = null;
}
return mDaemon;
}
@Nullable IUdfpsOverlayController getUdfpsOverlayController() {
return mUdfpsOverlayController;
}
private void scheduleLoadAuthenticatorIds() {
// Note that this can be performed on the scheduler (as opposed to being done immediately
// when the HAL is (re)loaded, since
// 1) If this is truly the first time it's being performed (e.g. system has just started),
// this will be run very early and way before any applications need to generate keys.
// 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
// just been reloaded), the framework already has a cache of the authenticatorIds. This
// is safe because authenticatorIds only change when A) new template has been enrolled,
// or B) all templates are removed.
mHandler.post(() -> {
for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
final int targetUserId = user.id;
if (!mAuthenticatorIds.containsKey(targetUserId)) {
scheduleUpdateActiveUserWithoutHandler(targetUserId, true /* force */);
}
}
});
}
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
scheduleUpdateActiveUserWithoutHandler(targetUserId, false /* force */);
}
/**
* Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
* handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
* invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
* this operation on the same lambda/runnable as those operations so that the ordering is
* correct.
*
* @param targetUserId Switch to this user, and update their authenticatorId
* @param force Always retrieve the authenticatorId, even if we are already the targetUserId
*/
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId, boolean force) {
final boolean hasEnrolled =
!getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId,
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
mCurrentUserId = targetUserId;
} else {
Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
}
}
});
}
private int getCurrentUser() {
return mCurrentUserId;
}
@Override
public boolean containsSensor(int sensorId) {
return mSensorProperties.sensorId == sensorId;
}
@Override
@NonNull
public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
final List<FingerprintSensorPropertiesInternal> properties = new ArrayList<>();
properties.add(mSensorProperties);
return properties;
}
@Nullable
@Override
public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) {
return mSensorProperties;
}
@Override
public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
userId, mContext.getOpPackageName(), sensorId, mLockoutTracker);
mScheduler.scheduleClientMonitor(client);
});
}
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
mSensorProperties.sensorId);
mScheduler.scheduleClientMonitor(client);
});
}
@Override
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, userId, opPackageName,
mSensorProperties.sensorId);
mScheduler.scheduleClientMonitor(client);
});
}
@Override
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
userId, hardwareAuthToken, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
enrollReason);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
mFingerprintStateCallback.onClientStarted(clientMonitor);
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
mFingerprintStateCallback.onClientFinished(clientMonitor, success);
if (success) {
// Update authenticatorIds
scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
true /* force */);
}
}
});
});
return id;
}
@Override
public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
int statsClient) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
mLazyDaemon, token, id, listener, userId, opPackageName,
mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
statsClient);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
return id;
}
@Override
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
restricted, opPackageName, cookie, false /* requireConfirmation */,
mSensorProperties.sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mLockoutTracker,
mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication, mSensorProperties);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@Override
public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication) {
final long id = mRequestCounter.incrementAndGet();
scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
return id;
}
@Override
public void startPreparedClient(int sensorId, int cookie) {
mHandler.post(() -> mScheduler.startPreparedClient(cookie));
}
@Override
public void cancelAuthentication(int sensorId, @NonNull IBinder token, long requestId) {
Slog.d(TAG, "cancelAuthentication, sensorId: " + sensorId);
mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId));
}
@Override
public void scheduleRemove(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
@Override
public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
// For IBiometricsFingerprint@2.1, remove(0) means remove all enrollments
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
0 /* fingerprintId */, userId, opPackageName,
FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
}
private void scheduleInternalCleanup(int userId,
@Nullable BaseClientMonitor.Callback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final List<Fingerprint> enrolledList = getEnrolledFingerprints(
mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId, enrolledList,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, callback);
});
}
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable BaseClientMonitor.Callback callback) {
scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback,
mFingerprintStateCallback));
}
@Override
public boolean isHardwareDetected(int sensorId) {
return getDaemon() != null;
}
@Override
public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
mHandler.post(() -> {
FingerprintUtils.getLegacyInstance(mSensorId)
.renameBiometricForUser(mContext, userId, fingerId, name);
});
}
@Override
@NonNull
public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
return FingerprintUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId);
}
@Override
@LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
}
@Override
public long getAuthenticatorId(int sensorId, int userId) {
return mAuthenticatorIds.getOrDefault(userId, 0L);
}
@Override
public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
return;
}
final Udfps udfps = (Udfps) client;
udfps.onPointerDown(x, y, minor, major);
}
@Override
public void onPointerUp(int sensorId) {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
return;
}
final Udfps udfps = (Udfps) client;
udfps.onPointerUp();
}
@Override
public void onUiReady(int sensorId) {
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onUiReady received during client: " + client);
return;
}
final Udfps udfps = (Udfps) client;
udfps.onUiReady();
}
@Override
public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
mUdfpsOverlayController = controller;
}
@Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
mSidefpsController = controller;
}
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer) {
final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
if (mSensorProperties.isAnyUdfpsType()) {
proto.write(SensorStateProto.MODALITY_FLAGS, SensorStateProto.FINGERPRINT_UDFPS);
}
proto.write(SensorStateProto.CURRENT_STRENGTH,
Utils.getCurrentStrength(mSensorProperties.sensorId));
proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer));
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
final long userToken = proto.start(SensorStateProto.USER_STATES);
proto.write(UserStateProto.USER_ID, userId);
proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN,
mSensorProperties.resetLockoutRequiresHardwareAuthToken);
proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE,
mSensorProperties.resetLockoutRequiresChallenge);
proto.end(sensorToken);
}
@Override
public void dumpProtoMetrics(int sensorId, FileDescriptor fd) {
PerformanceTracker tracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
final ProtoOutputStream proto = new ProtoOutputStream(fd);
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
proto.write(FingerprintUserStatsProto.USER_ID, userId);
proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
FingerprintUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId).size());
// Normal fingerprint authentications (e.g. lockscreen)
long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptForUser(userId));
proto.write(PerformanceStatsProto.REJECT, tracker.getRejectForUser(userId));
proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireForUser(userId));
proto.write(PerformanceStatsProto.LOCKOUT, tracker.getTimedLockoutForUser(userId));
proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT,
tracker.getPermanentLockoutForUser(userId));
proto.end(countsToken);
// Statistics about secure fingerprint transactions (e.g. to unlock password
// storage, make secure purchases, etc.)
countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
proto.write(PerformanceStatsProto.ACCEPT, tracker.getAcceptCryptoForUser(userId));
proto.write(PerformanceStatsProto.REJECT, tracker.getRejectCryptoForUser(userId));
proto.write(PerformanceStatsProto.ACQUIRE, tracker.getAcquireCryptoForUser(userId));
proto.write(PerformanceStatsProto.LOCKOUT, 0); // meaningless for crypto
proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, 0); // meaningless for crypto
proto.end(countsToken);
proto.end(userToken);
}
proto.flush();
tracker.clear();
}
@Override
public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
// TODO (b/179101888): Remove this temporary workaround.
try {
callback.onCompleted();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to complete InvalidateAuthenticatorId");
}
}
@Override
public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
JSONObject dump = new JSONObject();
try {
dump.put("service", TAG);
dump.put("isUdfps", mIsUdfps);
dump.put("isPowerbuttonFps", mIsPowerbuttonFps);
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
final int N = FingerprintUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId).size();
JSONObject set = new JSONObject();
set.put("id", userId);
set.put("count", N);
set.put("accept", performanceTracker.getAcceptForUser(userId));
set.put("reject", performanceTracker.getRejectForUser(userId));
set.put("acquire", performanceTracker.getAcquireForUser(userId));
set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
// cryptoStats measures statistics about secure fingerprint transactions
// (e.g. to unlock password storage, make secure purchases, etc.)
set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
sets.put(set);
}
dump.put("prints", sets);
} catch (JSONException e) {
Slog.e(TAG, "dump formatting failure", e);
}
pw.println(dump);
pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
mScheduler.dump(pw);
}
void setTestHalEnabled(boolean enabled) {
mTestHalEnabled = enabled;
}
@NonNull
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName) {
return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback,
mFingerprintStateCallback, this, mHalResultController);
}
}