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

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

6/n: Decouple remainder of lockout stuff

Split/rename LockoutTracker into interface + implementation. This
allows us to have different implementations (framework implementation,
HAL implementation) that can be passed to AuthenticationClient. This
lets us 1) Compartmentalize lockout tracking, and 2) Decouple the
AuthenticationClient from relying on FingerprintService/FaceService's
member variables.

This change also removes the need for AuthenticationCliemtImpl. A
following change will decouple FingerprintAuthClient and
FaceAuthClient from FingerprintService/FaceService entirely.

Bug: 157790417
Test: Lockout on primary/secondary users

Change-Id: I25ff870d8e05f36c01601ab5f313477bcd42c137
parent 1147fedd
Loading
Loading
Loading
Loading
+24 −10
Original line number Diff line number Diff line
@@ -37,13 +37,8 @@ import java.util.ArrayList;
 */
public abstract class AuthenticationClient extends ClientMonitor {

    public abstract int handleFailedAttempt(int userId);
    public void resetFailedAttempts(int userId) {}

    public static final int LOCKOUT_NONE = 0;
    public static final int LOCKOUT_TIMED = 1;
    public static final int LOCKOUT_PERMANENT = 2;

    private final boolean mIsStrongBiometric;
    private final long mOpId;
    private final boolean mShouldFrameworkHandleLockout;
@@ -51,6 +46,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
    private final IActivityTaskManager mActivityTaskManager;
    private final TaskStackListener mTaskStackListener;
    private final PowerManager mPowerManager;
    private final LockoutTracker mLockoutTracker;
    private final Surface mSurface;

    // We need to track this state since it's possible for applications to request for
@@ -90,7 +86,8 @@ public abstract class AuthenticationClient extends ClientMonitor {
            boolean shouldFrameworkHandleLockout, boolean restricted, String owner, int cookie,
            boolean requireConfirmation, int sensorId, boolean isStrongBiometric, int statsModality,
            int statsClient, IActivityTaskManager activityTaskManager,
            TaskStackListener taskStackListener, PowerManager powerManager, Surface surface) {
            TaskStackListener taskStackListener, PowerManager powerManager,
            LockoutTracker lockoutTracker, Surface surface) {
        super(context, constants, daemon, token, listener, targetUserId, groupId,
                restricted, owner, cookie, sensorId, statsModality,
                BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
@@ -101,9 +98,25 @@ public abstract class AuthenticationClient extends ClientMonitor {
        mActivityTaskManager = activityTaskManager;
        mTaskStackListener = taskStackListener;
        mPowerManager = powerManager;
        mLockoutTracker = lockoutTracker;
        mSurface = surface;
    }

    public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
        final @LockoutTracker.LockoutMode int lockoutMode =
                mLockoutTracker.getLockoutModeForUser(userId);
        final PerformanceTracker performanceTracker =
                PerformanceTracker.getInstanceForSensorId(getSensorId());

        if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
            performanceTracker.incrementPermanentLockoutForUser(userId);
        } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
            performanceTracker.incrementTimedLockoutForUser(userId);
        }

        return lockoutMode;
    }

    protected long getStartTimeMs() {
        return mStartTimeMs;
    }
@@ -222,12 +235,13 @@ public abstract class AuthenticationClient extends ClientMonitor {
                }

                // Allow system-defined limit of number of attempts before giving up
                final int lockoutMode = handleFailedAttempt(getTargetUserId());
                if (lockoutMode != LOCKOUT_NONE && mShouldFrameworkHandleLockout) {
                final @LockoutTracker.LockoutMode int lockoutMode =
                        handleFailedAttempt(getTargetUserId());
                if (lockoutMode != LockoutTracker.LOCKOUT_NONE && mShouldFrameworkHandleLockout) {
                    Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
                            + lockoutMode + ")");
                    stop(false);
                    final int errorCode = lockoutMode == LOCKOUT_TIMED
                    final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
                            ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
                            : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
                    onError(errorCode, 0 /* vendorCode */);
@@ -239,7 +253,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
                        listener.onAuthenticationFailed(getSensorId());
                    }
                }
                result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
                result = lockoutMode != LockoutTracker.LOCKOUT_NONE; // in a lockout mode
            }
        } catch (RemoteException e) {
            Slog.e(getLogTag(), "Remote exception", e);
+13 −46
Original line number Diff line number Diff line
@@ -84,11 +84,12 @@ public abstract class BiometricServiceBase extends SystemService

    private final Context mContext;
    private final String mKeyguardPackage;
    private final IActivityTaskManager mActivityTaskManager;
    protected final IActivityTaskManager mActivityTaskManager;
    public final PowerManager mPowerManager;
    private final UserManager mUserManager;
    private final MetricsLogger mMetricsLogger;
    private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
    protected final BiometricTaskStackListener mTaskStackListener =
            new BiometricTaskStackListener();
    private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
    private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();

@@ -191,41 +192,7 @@ public abstract class BiometricServiceBase extends SystemService
    /**
     * @return one of the AuthenticationClient LOCKOUT constants
     */
    protected abstract int getLockoutMode(int userId);

    protected abstract class AuthenticationClientImpl extends AuthenticationClient {

        public AuthenticationClientImpl(Context context, DaemonWrapper daemon,
                IBinder token, ClientMonitorCallbackConverter listener, int targetUserId,
                int groupId, long opId, boolean shouldFrameworkHandleLockout, boolean restricted,
                String owner, int cookie, boolean requireConfirmation, int sensorId,
                boolean isStrongBiometric, int statsModality, int statsClient, Surface surface) {
            super(context, getConstants(), daemon, token, listener, targetUserId,
                    groupId, opId, shouldFrameworkHandleLockout, restricted, owner, cookie,
                    requireConfirmation, sensorId, isStrongBiometric,
                    statsModality, statsClient, mActivityTaskManager, mTaskStackListener,
                    mPowerManager, surface);
        }

        @Override
        public int handleFailedAttempt(int userId) {
            final int lockoutMode = getLockoutMode(userId);
            PerformanceTracker performanceTracker = PerformanceTracker.getInstanceForSensorId(
                    BiometricServiceBase.this.getSensorId());

            if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
                performanceTracker.incrementPermanentLockoutForUser(userId);
            } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
                performanceTracker.incrementTimedLockoutForUser(userId);
            }

            // Failing multiple times will continue to push out the lockout time
            if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
                return lockoutMode;
            }
            return AuthenticationClient.LOCKOUT_NONE;
        }
    }
    protected abstract @LockoutTracker.LockoutMode int getLockoutMode(int userId);

    /**
     * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
@@ -455,7 +422,7 @@ public abstract class BiometricServiceBase extends SystemService

        if (client instanceof AuthenticationClient) {
            final int userId = client.getTargetUserId();
            if (getLockoutMode(userId) == AuthenticationClient.LOCKOUT_NONE) {
            if (getLockoutMode(userId) == LockoutTracker.LOCKOUT_NONE) {
                mPerformanceTracker.incrementAcquireForUser(userId, client.isCryptoOperation());
            }
        }
@@ -570,7 +537,7 @@ public abstract class BiometricServiceBase extends SystemService
        });
    }

    protected void authenticateInternal(AuthenticationClientImpl client, long opId,
    protected void authenticateInternal(AuthenticationClient client, long opId,
            String opPackageName) {
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
@@ -578,7 +545,7 @@ public abstract class BiometricServiceBase extends SystemService
        authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
    }

    protected void authenticateInternal(AuthenticationClientImpl client, long opId,
    protected void authenticateInternal(AuthenticationClient client, long opId,
            String opPackageName, int callingUid, int callingPid, int callingUserId) {
        if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
                callingUserId)) {
@@ -658,15 +625,15 @@ public abstract class BiometricServiceBase extends SystemService
    }

    // Should be done on a handler thread - not on the Binder's thread.
    private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
    private void startAuthentication(AuthenticationClient client, String opPackageName) {
        if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");

        int lockoutMode = getLockoutMode(client.getTargetUserId());
        if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
        @LockoutTracker.LockoutMode int lockoutMode = getLockoutMode(client.getTargetUserId());
        if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
            Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
            int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
                    BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
                    BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
            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");
            }
+37 −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.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Interface for retrieval of current user's lockout state.
 */
public interface LockoutTracker {
    int LOCKOUT_NONE = 0;
    int LOCKOUT_TIMED = 1;
    int LOCKOUT_PERMANENT = 2;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({LOCKOUT_NONE, LOCKOUT_TIMED, LOCKOUT_PERMANENT})
    @interface LockoutMode {}

    @LockoutMode int getLockoutModeForUser(int userId);
}
+17 −13
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.Constants;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalClient;

@@ -209,7 +210,7 @@ public class FaceService extends BiometricServiceBase {
        }
    }

    private final class FaceAuthClient extends AuthenticationClientImpl {
    private final class FaceAuthClient extends AuthenticationClient {
        private int mLastAcquire;

        public FaceAuthClient(Context context,
@@ -217,10 +218,11 @@ public class FaceService extends BiometricServiceBase {
                ClientMonitorCallbackConverter listener, int targetUserId, int groupId, long opId,
                boolean restricted, String owner, int cookie, boolean requireConfirmation,
                int statsClient, Surface surface) {
            super(context, daemon, token, listener, targetUserId, groupId, opId,
            super(context, getConstants(), daemon, token, listener, targetUserId, groupId, opId,
                    false /* shouldFrameworkHandleLockout */, restricted, owner, cookie,
                    requireConfirmation, FaceService.this.getSensorId(), isStrongBiometric(),
                    statsModality(), statsClient, surface);
                    statsModality(), statsClient, mActivityTaskManager, mTaskStackListener,
                    mPowerManager, mLockoutTracker, surface);
        }

        @Override
@@ -420,7 +422,7 @@ public class FaceService extends BiometricServiceBase {
            final boolean restricted = isRestricted();
            final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
                    : BiometricsProtoEnums.CLIENT_UNKNOWN;
            final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
            final AuthenticationClient client = new FaceAuthClient(getContext(),
                    mDaemonWrapper, token, new ClientMonitorCallbackConverter(receiver),
                    mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
                    0 /* cookie */, false /* requireConfirmation */, statsClient,
@@ -436,7 +438,7 @@ public class FaceService extends BiometricServiceBase {
            checkPermission(USE_BIOMETRIC_INTERNAL);
            updateActiveGroup(groupId, opPackageName);
            final boolean restricted = true; // BiometricPrompt is always restricted
            final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
            final AuthenticationClient client = new FaceAuthClient(getContext(),
                    mDaemonWrapper, token,
                    new ClientMonitorCallbackConverter(sensorReceiver),
                    mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
@@ -702,14 +704,13 @@ public class FaceService extends BiometricServiceBase {
        }
    }

    private final LockoutHalImpl mLockoutTracker;
    private final FaceConstants mFaceConstants = new FaceConstants();

    @GuardedBy("this")
    private IBiometricsFace mDaemon;
    private UsageStats mUsageStats;
    private boolean mRevokeChallengePending = false;
    // One of the AuthenticationClient constants
    private int mCurrentUserLockoutMode;

    private NotificationManager mNotificationManager;

@@ -823,13 +824,15 @@ public class FaceService extends BiometricServiceBase {
        public void onLockoutChanged(long duration) {
            Slog.d(TAG, "onLockoutChanged: " + duration);

            final @LockoutTracker.LockoutMode int lockoutMode;
            if (duration == 0) {
                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
                lockoutMode = LockoutTracker.LOCKOUT_NONE;
            } else if (duration == -1 || duration == Long.MAX_VALUE) {
                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
                lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
            } else {
                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
                lockoutMode = LockoutTracker.LOCKOUT_TIMED;
            }
            mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);

            mHandler.post(() -> {
                if (duration == 0) {
@@ -929,6 +932,7 @@ public class FaceService extends BiometricServiceBase {

    public FaceService(Context context) {
        super(context);
        mLockoutTracker = new LockoutHalImpl();

        final boolean ignoreKeyguardBlacklist = Settings.Secure.getInt(context.getContentResolver(),
                SKIP_KEYGUARD_ACQUIRE_IGNORE_LIST, 0) != 0;
@@ -1050,7 +1054,7 @@ public class FaceService extends BiometricServiceBase {
    protected void handleUserSwitching(int userId) {
        super.handleUserSwitching(userId);
        // Will be updated when we get the callback from HAL
        mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
        mLockoutTracker.setCurrentUserLockoutMode(LockoutTracker.LOCKOUT_NONE);
    }

    @Override
@@ -1093,8 +1097,8 @@ public class FaceService extends BiometricServiceBase {
    }

    @Override
    protected int getLockoutMode(int userId) {
        return mCurrentUserLockoutMode;
    protected @LockoutTracker.LockoutMode int getLockoutMode(int userId) {
        return mLockoutTracker.getLockoutModeForUser(userId);
    }

    /** Gets the face daemon */
+37 −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.face;

import com.android.server.biometrics.sensors.LockoutTracker;

/**
 * Implementation for storing {@link android.hardware.biometrics.face.V1_0} lockout state.
 * This implementation notifies the framework of the current user's lockout state whenever
 * the user changes.
 */
public class LockoutHalImpl implements LockoutTracker {
    private @LockoutMode int mCurrentUserLockoutMode;

    @Override
    public int getLockoutModeForUser(int userId) {
        return mCurrentUserLockoutMode;
    }

    public void setCurrentUserLockoutMode(@LockoutMode int lockoutMode) {
        mCurrentUserLockoutMode = lockoutMode;
    }
}
Loading