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

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

10/n: Split ClientMonitor functionality into appropriate subclasses

Bug: 157790417

Test: atest BiometricServiceBaseTest
Test: Enroll, authenticate, remove on fingerprint/face devices
Change-Id: Icb4fa528e4c5878c6040632e577edd8de375953e
parent cf61afd7
Loading
Loading
Loading
Loading
+114 −0
Original line number Diff line number Diff line
/*
 * 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;

import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;

/**
 * Abstract {@link ClientMonitor} subclass that operations eligible/interested in acquisition
 * messages should extend.
 */
public abstract class AcquisitionClient extends ClientMonitor {

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

    private static final AudioAttributes VIBRATION_SONFICATION_ATTRIBUTES =
            new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                    .build();

    private final PowerManager mPowerManager;
    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,
                statsModality, statsAction, statsClient);
        mPowerManager = context.getSystemService(PowerManager.class);
        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
    }

    /**
     * Called when we get notification from the biometric's HAL that an image has been acquired.
     * Common to authenticate and enroll.
     * @param acquiredInfo info about the current image acquisition
     * @return true if client should be removed
     */
    public boolean onAcquired(int acquiredInfo, int vendorCode) {
        // Default is to always send acquire messages to clients.
        return onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
    }

    protected final boolean onAcquiredInternal(int acquiredInfo, int vendorCode,
            boolean shouldSend) {
        super.logOnAcquired(getContext(), acquiredInfo, vendorCode, getTargetUserId());
        if (DEBUG) {
            Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
                    + ", shouldSend: " + shouldSend);
        }

        try {
            if (getListener() != null && shouldSend) {
                getListener().onAcquired(getSensorId(), acquiredInfo, vendorCode);
            }
            return false; // acquisition continues...
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendAcquired", e);
            return true;
        } finally {
            // Good scans will keep the device awake
            if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                notifyUserActivity();
            }
        }
    }

    final void notifyUserActivity() {
        long now = SystemClock.uptimeMillis();
        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
    }


    final void vibrateSuccess() {
        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
        if (vibrator != null) {
            vibrator.vibrate(mSuccessVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES);
        }
    }

    protected final void vibrateError() {
        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
        if (vibrator != null) {
            vibrator.vibrate(mErrorVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES);
        }
    }
}
+5 −37
Original line number Diff line number Diff line
@@ -24,9 +24,7 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.security.KeyStore;
import android.util.Slog;
import android.view.Surface;
@@ -36,7 +34,7 @@ import java.util.ArrayList;
/**
 * A class to keep track of the authentication state for a given client.
 */
public abstract class AuthenticationClient extends ClientMonitor {
public abstract class AuthenticationClient extends AcquisitionClient {

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

@@ -45,7 +43,6 @@ public abstract class AuthenticationClient extends ClientMonitor {
    private final boolean mRequireConfirmation;
    private final IActivityTaskManager mActivityTaskManager;
    private final TaskStackListener mTaskStackListener;
    private final PowerManager mPowerManager;
    private final LockoutTracker mLockoutTracker;
    private final Surface mSurface;

@@ -74,11 +71,10 @@ public abstract class AuthenticationClient extends ClientMonitor {
        }
    }

    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,
    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,
@@ -88,7 +84,6 @@ public abstract class AuthenticationClient extends ClientMonitor {
        mRequireConfirmation = requireConfirmation;
        mActivityTaskManager = ActivityTaskManager.getService();
        mTaskStackListener = taskStackListener;
        mPowerManager = context.getSystemService(PowerManager.class);
        mLockoutTracker = lockoutTracker;
        mSurface = surface;
    }
@@ -112,12 +107,6 @@ public abstract class AuthenticationClient extends ClientMonitor {
        return mStartTimeMs;
    }

    @Override
    public void notifyUserActivity() {
        long now = SystemClock.uptimeMillis();
        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
    }

    @Override
    public void binderDied() {
        final boolean clearListener = !isBiometricPrompt();
@@ -133,7 +122,6 @@ public abstract class AuthenticationClient extends ClientMonitor {
        return mOpId != 0;
    }

    @Override
    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
            boolean authenticated, ArrayList<Byte> token) {
        super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
@@ -266,24 +254,4 @@ public abstract class AuthenticationClient extends ClientMonitor {
        mAlreadyCancelled = true;
        return 0; // success
    }

    @Override
    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
            int remaining) {
        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
        return true; // Invalid for Authenticate
    }

    @Override
    public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
        return true; // Invalid for Authenticate
    }

    @Override
    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
            int remaining) {
        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
        return true; // Invalid for Authenticate
    }
}
+59 −25
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ public abstract class BiometricServiceBase extends SystemService
    private final Context mContext;
    private final String mKeyguardPackage;
    protected final IActivityTaskManager mActivityTaskManager;
    public final PowerManager mPowerManager;
    private final PowerManager mPowerManager;
    private final UserManager mUserManager;
    protected final BiometricTaskStackListener mTaskStackListener =
            new BiometricTaskStackListener();
@@ -406,8 +406,15 @@ public abstract class BiometricServiceBase extends SystemService
     */

    protected void handleAcquired(int acquiredInfo, int vendorCode) {
        ClientMonitor client = mCurrentClient;
        if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
        final ClientMonitor client = mCurrentClient;
        if (!(client instanceof AcquisitionClient)) {
            final String clientName = client != null ? client.getClass().getSimpleName() : "null";
            Slog.e(getTag(), "handleAcquired for non-acquire consumer: " + clientName);
            return;
        }

        final AcquisitionClient acquisitionClient = (AcquisitionClient) client;
        if (acquisitionClient.onAcquired(acquiredInfo, vendorCode)) {
            removeClient(client);
        }

@@ -421,27 +428,40 @@ public abstract class BiometricServiceBase extends SystemService

    protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
            ArrayList<Byte> token) {
        ClientMonitor client = mCurrentClient;
        final ClientMonitor client = mCurrentClient;
        if (!(client instanceof AuthenticationClient)) {
            final String clientName = client != null ? client.getClass().getSimpleName() : "null";
            Slog.e(getTag(), "handleAuthenticated for non-authentication client: " + clientName);
            return;
        }

        final AuthenticationClient authenticationClient = (AuthenticationClient) client;
        final boolean authenticated = identifier.getBiometricId() != 0;

        if (client != null) {
            final int userId = client.getTargetUserId();
            if (client.isCryptoOperation()) {
        final int userId = authenticationClient.getTargetUserId();
        if (authenticationClient.isCryptoOperation()) {
            mPerformanceTracker.incrementCryptoAuthForUser(userId, authenticated);
        } else {
            mPerformanceTracker.incrementAuthForUser(userId, authenticated);
        }
            if (client.onAuthenticated(identifier, authenticated, token)) {
                removeClient(client);
            }
        if (authenticationClient.onAuthenticated(identifier, authenticated, token)) {
            removeClient(authenticationClient);
        }
    }

    protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
            int remaining) {
        ClientMonitor client = mCurrentClient;
        if (client != null && client.onEnrollResult(identifier, remaining)) {
            removeClient(client);
        final ClientMonitor client = mCurrentClient;
        if (!(client instanceof EnrollClient)) {
            final String clientName = client != null ? client.getClass().getSimpleName() : "null";
            Slog.e(getTag(), "handleEnrollResult for non-enroll client: " + clientName);
            return;
        }

        final EnrollClient enrollClient = (EnrollClient) client;

        if (enrollClient.onEnrollResult(identifier, remaining)) {
            removeClient(enrollClient);
            // When enrollment finishes, update this group's authenticator id, as the HAL has
            // already generated a new authenticator id when the new biometric is enrolled.
            if (identifier instanceof Fingerprint) {
@@ -456,7 +476,8 @@ public abstract class BiometricServiceBase extends SystemService
        if (DEBUG) Slog.v(getTag(), "handleError(client="
                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");

        if (client != null && client.onError(error, vendorCode)) {
        if (client != null) {
            client.onError(error, vendorCode);
            removeClient(client);
        }

@@ -477,8 +498,15 @@ public abstract class BiometricServiceBase extends SystemService
                + ", dev=" + identifier.getDeviceId()
                + ", rem=" + remaining);

        ClientMonitor client = mCurrentClient;
        if (client != null && client.onRemoved(identifier, remaining)) {
        final ClientMonitor client = mCurrentClient;
        if (!(client instanceof RemovalConsumer)) {
            final String clientName = client != null ? client.getClass().getSimpleName() : "null";
            Slog.e(getTag(), "handleRemoved for non-removal consumer: " + clientName);
            return;
        }

        final RemovalConsumer removalConsumer = (RemovalConsumer) client;
        if (removalConsumer.onRemoved(identifier, remaining)) {
            removeClient(client);
            // When the last biometric of a group is removed, update the authenticator id
            int userId = mCurrentUserId;
@@ -492,8 +520,16 @@ public abstract class BiometricServiceBase extends SystemService
    }

    protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
        ClientMonitor client = mCurrentClient;
        if (client != null && client.onEnumerationResult(identifier, remaining)) {
        final ClientMonitor client = mCurrentClient;
        if (!(client instanceof EnumerateConsumer)) {
            final String clientName = client != null ? client.getClass().getSimpleName() : "null";
            Slog.e(getTag(), "handleEnumerate for non-enumerate consumer: "
                    + clientName);
            return;
        }

        final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
        if (enumerateConsumer.onEnumerationResult(identifier, remaining)) {
            removeClient(client);
        }
    }
@@ -624,9 +660,7 @@ public abstract class BiometricServiceBase extends SystemService
            int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
                    ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
                    : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
            if (!client.onError(errorCode, 0 /* vendorCode */)) {
                Slog.w(getTag(), "Cannot send permanent lockout message to client");
            }
            client.onError(errorCode, 0 /* vendorCode */);
            return;
        }
        startClient(client, true /* initiatedByClient */);
+9 −88
Original line number Diff line number Diff line
@@ -17,16 +17,11 @@
package com.android.server.biometrics.sensors;

import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;

import java.util.ArrayList;
import java.util.NoSuchElementException;

/**
@@ -38,13 +33,8 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D

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

    protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
    static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
    protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
            new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                    .build();

    private final Context mContext;
    private final int mTargetUserId;
@@ -52,8 +42,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 VibrationEffect mSuccessVibrationEffect;
    private final VibrationEffect mErrorVibrationEffect;
    private final BiometricServiceBase.DaemonWrapper mDaemon;
    private final int mSensorId; // sensorId as configured by the framework

@@ -63,8 +51,8 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    // is never 0.
    private final int mCookie;

    protected boolean mAlreadyCancelled;
    protected boolean mAlreadyDone;
    boolean mAlreadyCancelled;
    boolean mAlreadyDone;

    /**
     * @param context context of BiometricService
@@ -77,11 +65,10 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
     * permission
     * @param owner name of the client that owns this
     */
    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(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(statsModality, statsAction, statsClient);
        mContext = context;
        mDaemon = daemon;
@@ -93,8 +80,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
        mOwner = owner;
        mCookie = cookie;
        mSensorId = sensorId;
        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);

        try {
            if (token != null) {
                token.linkToDeath(this, 0);
@@ -120,67 +106,17 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
     */
    public abstract int stop(boolean initiatedByClient);

    /**
     * Method to explicitly poke powermanager on events
     */
    public abstract void notifyUserActivity();

    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
    // respective client (e.g. enrolling shouldn't get authenticate events).
    // All of these return 'true' if the operation is completed and it's ok to move
    // to the next client (e.g. authentication accepts or rejects a biometric).
    public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
            int remaining);
    public abstract boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
            boolean authenticated, ArrayList<Byte> token);
    public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
            int remaining);
    public abstract boolean onEnumerationResult(
            BiometricAuthenticator.Identifier identifier, int remaining);

    public boolean isAlreadyDone() {
        return mAlreadyDone;
    }

    /**
     * Called when we get notification from the biometric's HAL that an image has been acquired.
     * Common to authenticate and enroll.
     * @param acquiredInfo info about the current image acquisition
     * @return true if client should be removed
     */
    public boolean onAcquired(int acquiredInfo, int vendorCode) {
        // Default is to always send acquire messages to clients.
        return onAcquiredInternal(acquiredInfo, vendorCode, true /* shouldSend */);
    }

    protected final boolean onAcquiredInternal(int acquiredInfo, int vendorCode,
            boolean shouldSend) {
        super.logOnAcquired(mContext, acquiredInfo, vendorCode, getTargetUserId());
        if (DEBUG) Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
                + ", shouldSend: " + shouldSend);
        try {
            if (mListener != null && shouldSend) {
                mListener.onAcquired(mSensorId, acquiredInfo, vendorCode);
            }
            return false; // acquisition continues...
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendAcquired", e);
            return true;
        } finally {
            // Good scans will keep the device awake
            if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
                notifyUserActivity();
            }
        }
    }

    /**
     * Called when we get notification from the biometric's HAL that an error has occurred with the
     * current operation. Common to authenticate, enroll, enumerate and remove.
     * @param error
     * @return true if client should be removed
     */
    public boolean onError(int error, int vendorCode) {
    public void onError(int error, int vendorCode) {
        super.logOnError(mContext, error, vendorCode, getTargetUserId());
        try {
            if (mListener != null) {
@@ -189,7 +125,6 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to invoke sendError", e);
        }
        return true; // errors always remove current client
    }

    public void destroy() {
@@ -268,18 +203,4 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
    public final int getSensorId() {
        return mSensorId;
    }

    public final void vibrateSuccess() {
        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
        if (vibrator != null) {
            vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
        }
    }

    public final void vibrateError() {
        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
        if (vibrator != null) {
            vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
        }
    }
}
+10 −46

File changed.

Preview size limit exceeded, changes collapsed.

Loading