Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0191004 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

13/n: Remove the use of DaemonWrapper

The DaemonWrapper forces that all clients implement the same interface.
This makes it very hard to implement HIDL-specific functionality without
affecting unrelated implementations (e.g. groupId exists only for
fingerprint, enrollment surface is only used for face).

This change does the following high level things:
1) ClientMonitor now has abstract methods to startHalOperation() and
   stopHalOperation()
2) DaemonWrapper is removed. As a result, ClientMonitor invokes the
   above abstract methods when a HAL operation must be performed
3) Similarly, introduces Fingerprint*Clients and Face*Clients
   that are constructed with instances of fingerprint and face HAL.

A follow-up CL will remove the remainder of coupling in the ClientMonitor
base class (such as groupId, surface, etc).

Similarly, a follow-up CL will move the HAL operation scheduling to a
common shared area. This can be done once all HAL operations are
encapsulated in a ClientMonitor subclass. We can then ensure that all
operations are performed/scheduled properly. For example, internal
cleanup is not scheduled properly right now. The scheduler will be
agnostic to implementation details and should be designed to work
based on on a small set of properties that can describe all HIDL
operations (such as cancellable/non-cancellable, and
synchronous/asynchronous)

Also fixed some misnaming (groupId is actually userId in many places).
Unrelated to the misnaming issue, GroupId is being removed in a later
CL since it's never used, and actually is the same as userId.
(See FingerprintService.FingerprintServiceWrapper#enroll).

Bug: 157790417
Test: On fingerprint / face devices do the following tests:
      1) Modify fingerprint/face utils to have extra/missing templates,
         reboot device, see dangling/extra templates removed
      2) Enroll, auth, rename, remove for multiple users (work profile).
         Auth includes keyguard, BiometricPromptDemo

Change-Id: I3d3565b0cbf77155916a6116e92aced6a8ccc05d
parent d60191db
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.biometrics.sensors;

import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
@@ -45,12 +46,11 @@ public abstract class AcquisitionClient extends ClientMonitor {
    private final VibrationEffect mSuccessVibrationEffect;
    private final VibrationEffect mErrorVibrationEffect;

    AcquisitionClient(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
            ClientMonitorCallbackConverter listener, int userId, int groupId, boolean restricted,
            String owner, int cookie, int sensorId, int statsModality, int statsAction,
            int statsClient) {
        super(context, daemon, token, listener, userId, groupId, restricted, owner, cookie,
                sensorId,
    AcquisitionClient(@NonNull Context context, @NonNull IBinder token,
            @NonNull ClientMonitorCallbackConverter listener, int userId, int groupId,
            boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
            int statsAction, int statsClient) {
        super(context, token, listener, userId, groupId, restricted, owner, cookie, sensorId,
                statsModality, statsAction, statsClient);
        mPowerManager = context.getSystemService(PowerManager.class);
        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+34 −44
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.biometrics.sensors;

import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
@@ -27,7 +28,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.security.KeyStore;
import android.util.Slog;
import android.view.Surface;

import java.util.ArrayList;

@@ -39,53 +39,29 @@ public abstract class AuthenticationClient extends AcquisitionClient {
    private static final String TAG = "Biometrics/AuthenticationClient";

    private final boolean mIsStrongBiometric;
    private final long mOpId;
    private final boolean mRequireConfirmation;
    private final IActivityTaskManager mActivityTaskManager;
    private final TaskStackListener mTaskStackListener;
    private final LockoutTracker mLockoutTracker;
    private final Surface mSurface;

    private long mStartTimeMs;

    /**
     * This method is called when authentication starts.
     */
    protected void onStart() {
        try {
            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
        } catch (RemoteException e) {
            Slog.e(TAG, "Could not register task stack listener", e);
        }
    }
    protected final long mOperationId;

    /**
     * This method is called when a biometric is authenticated or authentication is stopped
     * (cancelled by the user, or an error such as lockout has occurred).
     */
    protected void onStop() {
        try {
            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
        } catch (RemoteException e) {
            Slog.e(TAG, "Could not unregister task stack listener", e);
        }
    }
    private long mStartTimeMs;

    public AuthenticationClient(Context context, BiometricServiceBase.DaemonWrapper daemon,
            IBinder token, ClientMonitorCallbackConverter listener, int targetUserId, int groupId,
            long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation,
            int sensorId, boolean isStrongBiometric, int statsModality, int statsClient,
            TaskStackListener taskStackListener, LockoutTracker lockoutTracker, Surface surface) {
        super(context, daemon, token, listener, targetUserId, groupId,
                restricted, owner, cookie, sensorId, statsModality,
                BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
    public AuthenticationClient(@NonNull Context context, @NonNull IBinder token,
            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, int groupId,
            long operationId, boolean restricted, @NonNull String owner, int cookie,
            boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsModality,
            int statsClient, @NonNull TaskStackListener taskStackListener,
            @NonNull LockoutTracker lockoutTracker) {
        super(context, token, listener, targetUserId, groupId, restricted, owner, cookie, sensorId,
                statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
        mIsStrongBiometric = isStrongBiometric;
        mOpId = opId;
        mOperationId = operationId;
        mRequireConfirmation = requireConfirmation;
        mActivityTaskManager = ActivityTaskManager.getService();
        mTaskStackListener = taskStackListener;
        mLockoutTracker = lockoutTracker;
        mSurface = surface;
    }

    public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
@@ -119,7 +95,7 @@ public abstract class AuthenticationClient extends AcquisitionClient {

    @Override
    protected boolean isCryptoOperation() {
        return mOpId != 0;
        return mOperationId != 0;
    }

    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
@@ -147,7 +123,12 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                    vibrateSuccess();
                }
                result = true;
                onStop();

                try {
                    mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Could not unregister task stack listener", e);
                }

                final byte[] byteToken = new byte[token.size()];
                for (int i = 0; i < token.size(); i++) {
@@ -212,10 +193,15 @@ public abstract class AuthenticationClient extends AcquisitionClient {
     */
    @Override
    public int start() {
        onStart();
        try {
            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
        } catch (RemoteException e) {
            Slog.e(TAG, "Could not register task stack listener", e);
        }

        try {
            mStartTimeMs = System.currentTimeMillis();
            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId(), mSurface);
            final int result = startHalOperation();
            if (result != 0) {
                Slog.w(TAG, "startAuthentication failed, result=" + result);
                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
@@ -224,7 +210,7 @@ public abstract class AuthenticationClient extends AcquisitionClient {
            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
        } catch (RemoteException e) {
            Slog.e(TAG, "startAuthentication failed", e);
            return ERROR_ESRCH;
            return BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS;
        }
        return 0; // success
    }
@@ -236,10 +222,14 @@ public abstract class AuthenticationClient extends AcquisitionClient {
            return 0;
        }

        onStop();
        try {
            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
        } catch (RemoteException e) {
            Slog.e(TAG, "Could not unregister task stack listener", e);
        }

        try {
            final int result = getDaemonWrapper().cancel();
            final int result = stopHalOperation();
            if (result != 0) {
                Slog.w(TAG, "stopAuthentication failed, result=" + result);
                return result;
@@ -248,7 +238,7 @@ public abstract class AuthenticationClient extends AcquisitionClient {
                    " is no longer authenticating");
        } catch (RemoteException e) {
            Slog.e(TAG, "stopAuthentication failed", e);
            return ERROR_ESRCH;
            return BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS;
        }

        mAlreadyCancelled = true;
+6 −40
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.view.Surface;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -77,7 +76,6 @@ public abstract class BiometricServiceBase extends SystemService

    protected static final boolean DEBUG = true;

    private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
    private static final int MSG_USER_SWITCHING = 10;
    private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms

@@ -124,11 +122,6 @@ public abstract class BiometricServiceBase extends SystemService
     */
    protected abstract String getTag();

    /**
     * @return wrapper for the HAL
     */
    protected abstract DaemonWrapper getDaemonWrapper();

    /**
     * @return the biometric utilities for a specific implementation.
     */
@@ -187,22 +180,6 @@ public abstract class BiometricServiceBase extends SystemService
     */
    protected abstract @LockoutTracker.LockoutMode int getLockoutMode(int userId);

    /**
     * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
     * subclasses.
     */
    public interface DaemonWrapper {
        int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
        int authenticate(long operationId, int groupId, Surface surface)
                throws RemoteException;
        int cancel() throws RemoteException;
        int remove(int groupId, int biometricId) throws RemoteException;
        int enumerate() throws RemoteException;
        int enroll(byte[] token, int groupId, int timeout,
                ArrayList<Integer> disabledFeatures, Surface surface) throws RemoteException;
        void resetLockout(byte[] token) throws RemoteException;
    }

    private final Runnable mOnTaskStackChangedRunnable = new Runnable() {
        @Override
        public void run() {
@@ -636,6 +613,10 @@ public abstract class BiometricServiceBase extends SystemService

    protected void cleanupInternal(InternalCleanupClient client) {
        mHandler.post(() -> {
            if (DEBUG) {
                Slog.v(getTag(), "Cleaning up templates for user("
                        + client.getTargetUserId() + ")");
            }
            startClient(client, true /* initiatedByClient */);
        });
    }
@@ -925,7 +906,7 @@ public abstract class BiometricServiceBase extends SystemService
        return false;
    }

    /***
    /**
     * @return authenticator id for the calling user
     */
    protected long getAuthenticatorId(int callingUserId) {
@@ -935,23 +916,8 @@ public abstract class BiometricServiceBase extends SystemService

    /**
     * This method should be called upon connection to the daemon, and when user switches.
     * @param userId
     */
    protected void doTemplateCleanupForUser(int userId) {
        if (CLEANUP_UNKNOWN_TEMPLATES) {
            if (DEBUG) Slog.v(getTag(), "Cleaning up templates for user(" + userId + ")");

            final boolean restricted = !hasPermission(getManageBiometricPermission());
            final List<? extends BiometricAuthenticator.Identifier> enrolledList =
                    getEnrolledTemplates(userId);

            InternalCleanupClient client = new InternalCleanupClient(getContext(),
                    getDaemonWrapper(), null /* serviceListener */, userId, userId,
                    restricted, getContext().getOpPackageName(), getSensorId(), statsModality(),
                    enrolledList, getBiometricUtils());
            cleanupInternal(client);
        }
    }
    protected abstract void doTemplateCleanupForUser(int userId);

    /**
     * This method is called when the user switches. Implementations should probably notify the
+39 −22
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package com.android.server.biometrics.sensors;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -32,8 +35,6 @@ import java.util.NoSuchElementException;
public abstract class ClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient {

    private static final String TAG = "Biometrics/ClientMonitor";

    static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
    protected static final boolean DEBUG = BiometricServiceBase.DEBUG;

    private final Context mContext;
@@ -42,7 +43,6 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    // True if client does not have MANAGE_FINGERPRINT permission
    private final boolean mIsRestricted;
    private final String mOwner;
    private final BiometricServiceBase.DaemonWrapper mDaemon;
    private final int mSensorId; // sensorId as configured by the framework

    private IBinder mToken;
@@ -55,8 +55,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    boolean mAlreadyDone;

    /**
     * @param context context of BiometricService
     * @param daemon interface to call back to a specific biometric's daemon
     * @param context    system_server context
     * @param token      a unique token for the client
     * @param listener   recipient of related events (e.g. authentication)
     * @param userId     target user id for operation
@@ -64,14 +63,18 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
     * @param restricted whether or not client has the MANAGE_* permission
     *                   permission
     * @param owner      name of the client that owns this
     * @param cookie     BiometricPrompt authentication cookie (to be moved into a subclass soon)
     * @param sensorId   ID of the sensor that the operation should be requested of
     * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants
     * @param statsAction   One of {@link BiometricsProtoEnums} ACTION_* constants
     * @param statsClient   One of {@link BiometricsProtoEnums} CLIENT_* constants
     */
    public ClientMonitor(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
            ClientMonitorCallbackConverter listener, int userId, int groupId, boolean restricted,
            String owner, int cookie, int sensorId, int statsModality, int statsAction,
            int statsClient) {
    public ClientMonitor(@NonNull Context context, IBinder token,
            @Nullable ClientMonitorCallbackConverter listener, int userId, int groupId,
            boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
            int statsAction, int statsClient) {
        super(statsModality, statsAction, statsClient);
        mContext = context;
        mDaemon = daemon;
        mToken = token;
        mListener = listener;
        mTargetUserId = userId;
@@ -95,17 +98,35 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    }

    /**
     * Contacts the biometric's HAL to start the client.
     * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
     * keeping is complete.
     * @return 0 on success, errno from driver on failure
     */
    public abstract int start();

    /**
     * Contacts the biometric's HAL to stop the client.
     * Requests to end the ClientMonitor's lifecycle. Invokes {@link #stopHalOperation()} when
     * internal book keeping is complete.
     * @param initiatedByClient whether the operation is at the request of a client
     */
    public abstract int stop(boolean initiatedByClient);

    /**
     * Starts the HAL operation specific to the ClientMonitor subclass.
     * @return status code specific to the HIDL interface. ClientMonitor subclasses currently
     * assume that non-zero codes are errors, and 0 == success.
     * @throws RemoteException
     */
    protected abstract int startHalOperation() throws RemoteException;

    /**
     * Stops the HAL operation specific to the ClientMonitor subclass.
     * @return status code specific to the HIDL interface. ClientMonitor subclasses currently
     * assume that non-zero codes are errors, and 0 == success.
     * @throws RemoteException
     */
    protected abstract int stopHalOperation() throws RemoteException;

    public boolean isAlreadyDone() {
        return mAlreadyDone;
    }
@@ -180,10 +201,6 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
        return mListener;
    }

    public final BiometricServiceBase.DaemonWrapper getDaemonWrapper() {
        return mDaemon;
    }

    public final boolean getIsRestricted() {
        return mIsRestricted;
    }
+13 −24
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.biometrics.sensors;

import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -23,40 +24,34 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import android.view.Surface;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * A class to keep track of the enrollment state for a given client.
 */
public class EnrollClient extends AcquisitionClient {
public abstract class EnrollClient extends AcquisitionClient {

    private static final String TAG = "Biometrics/EnrollClient";

    private final byte[] mCryptoToken;
    protected final byte[] mHardwareAuthToken;
    protected final int mTimeoutSec;
    private final BiometricUtils mBiometricUtils;
    private final int[] mDisabledFeatures;
    private final int mTimeoutSec;
    private final Surface mSurface;
    private final boolean mShouldVibrate;

    private long mEnrollmentStartTimeMs;

    public EnrollClient(Context context, BiometricServiceBase.DaemonWrapper daemon, IBinder token,
            ClientMonitorCallbackConverter listener, int userId, int groupId, byte[] cryptoToken,
            boolean restricted, String owner, BiometricUtils utils, final int[] disabledFeatures,
            int timeoutSec, int statsModality, Surface surface, int sensorId,
    public EnrollClient(@NonNull Context context, @NonNull IBinder token,
            @NonNull ClientMonitorCallbackConverter listener, int userId, int groupId,
            @NonNull byte[] hardwareAuthToken, boolean restricted, String owner,
            @NonNull BiometricUtils utils, int timeoutSec, int statsModality, int sensorId,
            boolean shouldVibrate) {
        super(context, daemon, token, listener, userId, groupId, restricted,
                owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENROLL,
        super(context, token, listener, userId, groupId, restricted, owner, 0 /* cookie */,
                sensorId, statsModality, BiometricsProtoEnums.ACTION_ENROLL,
                BiometricsProtoEnums.CLIENT_UNKNOWN);
        mBiometricUtils = utils;
        mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
        mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
        mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
        mTimeoutSec = timeoutSec;
        mSurface = surface;
        mShouldVibrate = shouldVibrate;
    }

@@ -96,13 +91,7 @@ public class EnrollClient extends AcquisitionClient {
    public int start() {
        mEnrollmentStartTimeMs = System.currentTimeMillis();
        try {
            final ArrayList<Integer> disabledFeatures = new ArrayList<>();
            for (int i = 0; i < mDisabledFeatures.length; i++) {
                disabledFeatures.add(mDisabledFeatures[i]);
            }

            final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec,
                    disabledFeatures, mSurface);
            final int result = startHalOperation();
            if (result != 0) {
                Slog.w(TAG, "startEnroll failed, result=" + result);
                onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
@@ -122,7 +111,7 @@ public class EnrollClient extends AcquisitionClient {
        }

        try {
            final int result = getDaemonWrapper().cancel();
            final int result = stopHalOperation();
            if (result != 0) {
                Slog.w(TAG, "startEnrollCancel failed, result = " + result);
                return result;
Loading