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

Commit 10a39331 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

1/n: Move confirmationRequired logic into sub-modules

The logic for determining if AuthSession should request SystemUI to
display the confirmation button can be calculated purely within
AuthSession, making this much more modular, testable, and extendable.

A follow-up CL will remove the round-trip that requestConfirmation
takes (BiometricService -> <Biometric>Service -> BiometricService).
Cleaning that up will be easier once all of AuthSession state is
managed by itself, instead of BiometricService.

Bug: 149067920
Test: atest com.android.server.biometrics
Test: manual test on fingerprint and face devices

Change-Id: Ibc365b1992d23960bec0d66b818d27f4d086b462
parent 1e530243
Loading
Loading
Loading
Loading
+6 −7
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ final class AuthSession {
    final IBinder mToken;
    final long mOperationId;
    final int mUserId;
    final IBiometricSensorReceiver mSensorReceiver;
    private final IBiometricSensorReceiver mSensorReceiver;
    // Original receiver from BiometricPrompt.
    final IBiometricServiceReceiver mClientReceiver;
    final String mOpPackageName;
@@ -103,8 +103,6 @@ final class AuthSession {
    final int mCallingPid;
    final int mCallingUserId;

    final boolean mRequireConfirmation;

    // The current state, which can be either idle, called, or started
    @SessionState int mState = STATE_AUTH_IDLE;
    // For explicit confirmation, do not send to keystore until the user has confirmed
@@ -122,8 +120,7 @@ final class AuthSession {
    AuthSession(Random random, PreAuthInfo preAuthInfo, IBinder token, long operationId,
            int userId, IBiometricSensorReceiver sensorReceiver,
            IBiometricServiceReceiver clientReceiver, String opPackageName,
            Bundle bundle, int callingUid, int callingPid, int callingUserId,
            boolean requireConfirmation) {
            Bundle bundle, int callingUid, int callingPid, int callingUserId) {
        mRandom = random;
        mPreAuthInfo = preAuthInfo;
        mToken = token;
@@ -136,7 +133,6 @@ final class AuthSession {
        mCallingUid = callingUid;
        mCallingPid = callingPid;
        mCallingUserId = callingUserId;
        mRequireConfirmation = requireConfirmation;

        setSensorsToStateUnknown();
    }
@@ -162,7 +158,10 @@ final class AuthSession {
    void prepareAllSensorsForAuthentication() throws RemoteException {
        for (BiometricSensor sensor : mPreAuthInfo.eligibleSensors) {
            final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
            sensor.goToStateWaitingForCookie(mRequireConfirmation, mToken, mOperationId, mUserId,
            final boolean requireConfirmation = sensor.confirmationSupported()
                    && (sensor.confirmationAlwaysRequired(mUserId)
                    || mPreAuthInfo.confirmationRequested);
            sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId, mUserId,
                    mSensorReceiver, mOpPackageName, cookie, mCallingUid, mCallingPid,
                    mCallingUserId);
        }
+12 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy;
 * including its current state.
 * TODO(b/141025588): Consider refactoring the tests to not rely on this implementation detail.
 */
public class BiometricSensor {
public abstract class BiometricSensor {
    private static final String TAG = "BiometricService/Sensor";

    // State is unknown. Usually this means we need the sensor but have not requested for
@@ -71,6 +71,17 @@ public class BiometricSensor {

    private int mCookie; // invalid during STATE_UNKNOWN

    /**
     * @return true if the user's system settings specifies that this sensor always requires
     * confirmation.
     */
    abstract boolean confirmationAlwaysRequired(int userId);

    /**
     * @return true if confirmation is supported by this sensor.
     */
    abstract boolean confirmationSupported();

    BiometricSensor(int id, int modality, int strength,
            IBiometricAuthenticator impl) {
        this.id = id;
+28 −32
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.biometrics;

import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricManager.Authenticators;

@@ -322,14 +321,23 @@ public class BiometricService extends SystemService {
            return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
        }

        public boolean getFaceAlwaysRequireConfirmation(int userId) {
        public boolean getConfirmationAlwaysRequired(@BiometricAuthenticator.Modality int modality,
                int userId) {
            switch (modality) {
                case BiometricAuthenticator.TYPE_FACE:
                    if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
                onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
                        onChange(true /* selfChange */,
                                FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
                                userId);
                    }
                    return mFaceAlwaysRequireConfirmation.get(userId);

                default:
                    return false;
            }
        }

        public void notifyEnabledOnKeyguardCallbacks(int userId) {
        void notifyEnabledOnKeyguardCallbacks(int userId) {
            List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).notify(BiometricSourceType.FACE,
@@ -600,7 +608,17 @@ public class BiometricService extends SystemService {
                throw new IllegalStateException("Cannot register unknown id");
            }

            mSensors.add(new BiometricSensor(id, modality, strength, authenticator));
            mSensors.add(new BiometricSensor(id, modality, strength, authenticator) {
                @Override
                boolean confirmationAlwaysRequired(int userId) {
                    return mSettingObserver.getConfirmationAlwaysRequired(modality, userId);
                }

                @Override
                boolean confirmationSupported() {
                    return Utils.isConfirmationSupported(modality);
                }
            });

            mBiometricStrengthController.updateStrengths();
        }
@@ -833,15 +851,6 @@ public class BiometricService extends SystemService {
        return false;
    }

    private int biometricIdToModality(int id) {
        for (BiometricSensor sensor : mSensors) {
            if (sensor.id == id) {
                return sensor.modality;
            }
        }
        return TYPE_NONE;
    }

    private void logDialogDismissed(int reason) {
        if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
            // Explicit auth, authentication confirmed.
@@ -856,7 +865,7 @@ public class BiometricService extends SystemService {
                        + ", IsCrypto: " + mCurrentAuthSession.isCrypto()
                        + ", Client: " + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT
                        + ", RequireConfirmation: "
                        + mCurrentAuthSession.mRequireConfirmation
                        + mCurrentAuthSession.mPreAuthInfo.confirmationRequested
                        + ", State: " + FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED
                        + ", Latency: " + latency);
            }
@@ -866,7 +875,7 @@ public class BiometricService extends SystemService {
                    mCurrentAuthSession.mUserId,
                    mCurrentAuthSession.isCrypto(),
                    BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                    mCurrentAuthSession.mRequireConfirmation,
                    mCurrentAuthSession.mPreAuthInfo.confirmationRequested,
                    FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                    latency,
                    mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
@@ -1330,24 +1339,11 @@ public class BiometricService extends SystemService {
    private void authenticateInternal(IBinder token, long operationId, int userId,
            IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
            int callingUid, int callingPid, int callingUserId, PreAuthInfo preAuthInfo) {
        boolean requireConfirmation = bundle.getBoolean(
                BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
        // Assume at this point that if a sensor is contained in the eligible list, it will be
        // used for authentication and presented to the user.
        for (BiometricSensor sensor : preAuthInfo.eligibleSensors) {
            if (biometricIdToModality(sensor.id) == TYPE_FACE) {
                // Check if the user has forced confirmation to be required in Settings.
                requireConfirmation = requireConfirmation
                        || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
            }
        }

        Slog.d(TAG, "Creating authSession with authRequest: " + preAuthInfo);

        mPendingAuthSession = new AuthSession(mRandom, preAuthInfo, token, operationId, userId,
                mBiometricSensorReceiver, receiver, opPackageName, bundle, callingUid, callingPid,
                callingUserId, requireConfirmation);

                callingUserId);
        try {
            if (preAuthInfo.credentialRequested
                    && preAuthInfo.eligibleSensors.isEmpty()) {
+17 −11
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Pair;
@@ -73,8 +74,9 @@ class PreAuthInfo {
    // Sensors that can be used for this request (e.g. strong enough, enrolled, enabled).
    final List<BiometricSensor> eligibleSensors;
    // Sensors that cannot be used for this request. Pair<BiometricSensor, AuthenticatorStatus>
    final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
    private final List<Pair<BiometricSensor, Integer>> mIneligibleSensors;
    final boolean credentialAvailable;
    final boolean confirmationRequested;

    static PreAuthInfo create(ITrustManager trustManager,
            DevicePolicyManager devicePolicyManager,
@@ -82,6 +84,8 @@ class PreAuthInfo {
            List<BiometricSensor> sensors,
            int userId, Bundle bundle, String opPackageName, boolean checkDevicePolicyManager)
            throws RemoteException {
        final boolean confirmationRequested = bundle.getBoolean(
                BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
        final boolean biometricRequested = Utils.isBiometricRequested(bundle);
        final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
        final boolean credentialRequested = Utils.isCredentialRequested(bundle);
@@ -118,7 +122,7 @@ class PreAuthInfo {
        }

        return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
                eligibleSensors, ineligibleSensors, credentialAvailable);
                eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested);
    }

    /**
@@ -222,14 +226,16 @@ class PreAuthInfo {

    private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
            boolean credentialRequested, List<BiometricSensor> eligibleSensors,
            List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable) {
            List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
            boolean confirmationRequested) {
        mBiometricRequested = biometricRequested;
        mBiometricStrengthRequested = biometricStrengthRequested;
        this.credentialRequested = credentialRequested;

        this.eligibleSensors = eligibleSensors;
        this.ineligibleSensors = ineligibleSensors;
        this.mIneligibleSensors = ineligibleSensors;
        this.credentialAvailable = credentialAvailable;
        this.confirmationRequested = confirmationRequested;
    }

    /**
@@ -253,9 +259,9 @@ class PreAuthInfo {
                }
            } else {
                // Pick the first sensor error if it exists
                if (!ineligibleSensors.isEmpty()) {
                    modality |= ineligibleSensors.get(0).first.modality;
                    status = ineligibleSensors.get(0).second;
                if (!mIneligibleSensors.isEmpty()) {
                    modality |= mIneligibleSensors.get(0).first.modality;
                    status = mIneligibleSensors.get(0).second;
                } else {
                    modality |= TYPE_CREDENTIAL;
                    status = CREDENTIAL_NOT_ENROLLED;
@@ -269,9 +275,9 @@ class PreAuthInfo {
                 }
            } else {
                // Pick the first sensor error if it exists
                if (!ineligibleSensors.isEmpty()) {
                    modality |= ineligibleSensors.get(0).first.modality;
                    status = ineligibleSensors.get(0).second;
                if (!mIneligibleSensors.isEmpty()) {
                    modality |= mIneligibleSensors.get(0).first.modality;
                    status = mIneligibleSensors.get(0).second;
                } else {
                    modality |= TYPE_NONE;
                    status = BIOMETRIC_NO_HARDWARE;
@@ -374,7 +380,7 @@ class PreAuthInfo {
        string.append("}");

        string.append("\nIneligible:{");
        for (Pair<BiometricSensor, Integer> ineligible : ineligibleSensors) {
        for (Pair<BiometricSensor, Integer> ineligible : mIneligibleSensors) {
            string.append(ineligible.first).append(":").append(ineligible.second).append(" ");
        }
        string.append("}");
+24 −13
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;

import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
@@ -40,7 +41,7 @@ import android.provider.Settings;
import android.util.Slog;

public class Utils {
    public static boolean isDebugEnabled(Context context, int targetUserId) {
    static boolean isDebugEnabled(Context context, int targetUserId) {
        if (targetUserId == UserHandle.USER_NULL) {
            return false;
        }
@@ -61,7 +62,7 @@ public class Utils {
     * Combines {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with
     * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible enough.
     */
    public static void combineAuthenticatorBundles(Bundle bundle) {
    static void combineAuthenticatorBundles(Bundle bundle) {
        // Cache and remove explicit ALLOW_DEVICE_CREDENTIAL boolean flag from the bundle.
        final boolean deviceCredentialAllowed =
                bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false);
@@ -85,7 +86,7 @@ public class Utils {
     * @param authenticators composed of one or more values from {@link Authenticators}
     * @return true if device credential is allowed.
     */
    public static boolean isCredentialRequested(@Authenticators.Types int authenticators) {
    static boolean isCredentialRequested(@Authenticators.Types int authenticators) {
        return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
    }

@@ -93,7 +94,7 @@ public class Utils {
     * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
     * @return true if device credential is allowed.
     */
    public static boolean isCredentialRequested(Bundle bundle) {
    static boolean isCredentialRequested(Bundle bundle) {
        return isCredentialRequested(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
    }

@@ -103,7 +104,7 @@ public class Utils {
     * @param authenticators composed of one or more values from {@link Authenticators}
     * @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
     */
    public static int getPublicBiometricStrength(@Authenticators.Types int authenticators) {
    static int getPublicBiometricStrength(@Authenticators.Types int authenticators) {
        // Only biometrics WEAK and above are allowed to integrate with the public APIs.
        return authenticators & Authenticators.BIOMETRIC_WEAK;
    }
@@ -114,7 +115,7 @@ public class Utils {
     * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
     * @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
     */
    public static int getPublicBiometricStrength(Bundle bundle) {
    static int getPublicBiometricStrength(Bundle bundle) {
        return getPublicBiometricStrength(
                bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
    }
@@ -125,7 +126,7 @@ public class Utils {
     * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
     * @return true if biometric authentication is allowed.
     */
    public static boolean isBiometricRequested(Bundle bundle) {
    static boolean isBiometricRequested(Bundle bundle) {
        return getPublicBiometricStrength(bundle) != 0;
    }

@@ -134,7 +135,7 @@ public class Utils {
     * @param requestedStrength the strength that it must meet
     * @return true only if the sensor is at least as strong as the requested strength
     */
    public static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) {
    static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) {
        // Clear out any bits that are not reserved for biometric
        sensorStrength &= Authenticators.BIOMETRIC_MIN_STRENGTH;

@@ -160,7 +161,7 @@ public class Utils {
     * @param bundle
     * @return
     */
    public static boolean isValidAuthenticatorConfig(Bundle bundle) {
    static boolean isValidAuthenticatorConfig(Bundle bundle) {
        final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
        return isValidAuthenticatorConfig(authenticators);
    }
@@ -170,7 +171,7 @@ public class Utils {
     * @param authenticators
     * @return
     */
    public static boolean isValidAuthenticatorConfig(int authenticators) {
    static boolean isValidAuthenticatorConfig(int authenticators) {
        // The caller is not required to set the authenticators. But if they do, check the below.
        if (authenticators == 0) {
            return true;
@@ -213,7 +214,7 @@ public class Utils {
     * @param biometricConstantsCode see {@link BiometricConstants}
     * @return see {@link BiometricManager}
     */
    public static int biometricConstantsToBiometricManager(int biometricConstantsCode) {
    static int biometricConstantsToBiometricManager(int biometricConstantsCode) {
        final int biometricManagerCode;

        switch (biometricConstantsCode) {
@@ -253,7 +254,7 @@ public class Utils {
     *         BiometricPrompt.AuthenticationResult}.
     * @throws IllegalArgumentException if given an invalid dismissal reason.
     */
    public static @AuthenticationResultType int getAuthenticationTypeForResult(int reason) {
    static @AuthenticationResultType int getAuthenticationTypeForResult(int reason) {
        switch (reason) {
            case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
                return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
@@ -268,7 +269,7 @@ public class Utils {
    }


    public static int authenticatorStatusToBiometricConstant(
    static int authenticatorStatusToBiometricConstant(
            @PreAuthInfo.AuthenticatorStatus int status) {
        switch (status) {
            case BIOMETRIC_NO_HARDWARE:
@@ -294,4 +295,14 @@ public class Utils {
                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
        }
    }

    static boolean isConfirmationSupported(@BiometricAuthenticator.Modality int modality) {
        switch (modality) {
            case BiometricAuthenticator.TYPE_FACE:
            case BiometricAuthenticator.TYPE_IRIS:
                return true;
            default:
                return false;
        }
    }
}
Loading