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

Commit 25af604c authored by Joe Bolinger's avatar Joe Bolinger Committed by Automerger Merge Worker
Browse files

Merge "Run biometric cleanup on user switch." into tm-dev am: 3bc43d43

parents 8b265fc9 3bc43d43
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -268,4 +268,27 @@ public interface BiometricConstants {
     */
    int BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL = 1;

    /**
     * No lockout.
     * @hide
     */
    int BIOMETRIC_LOCKOUT_NONE = 0;
    /**
     * The biometric is in a temporary lockout state that will expire after some time.
     * @hide
     */
    int BIOMETRIC_LOCKOUT_TIMED = 1;
    /**
     * The biometric is locked out until a reset occurs. Resets are typically triggered by
     * successfully authenticating via a stronger method than the one that is locked out.
     * @hide
     */
    int BIOMETRIC_LOCKOUT_PERMANENT = 2;

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({BIOMETRIC_LOCKOUT_NONE, BIOMETRIC_LOCKOUT_TIMED, BIOMETRIC_LOCKOUT_PERMANENT})
    @interface LockoutMode {}
}
+17 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.hardware.face;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -672,6 +673,22 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
        return new ArrayList<>();
    }

    /**
     * @hide
     */
    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
    @BiometricConstants.LockoutMode
    public int getLockoutModeForUser(int sensorId, int userId) {
        if (mService != null) {
            try {
                return mService.getLockoutModeForUser(sensorId, userId);
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
        }
        return BIOMETRIC_LOCKOUT_NONE;
    }

    /**
     * @hide
     */
+18 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;

import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE;
@@ -41,6 +42,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricTestSession;
@@ -1094,6 +1096,22 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
        }
    }

    /**
     * @hide
     */
    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
    @BiometricConstants.LockoutMode
    public int getLockoutModeForUser(int sensorId, int userId) {
        if (mService != null) {
            try {
                return mService.getLockoutModeForUser(sensorId, userId);
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
        }
        return BIOMETRIC_LOCKOUT_NONE;
    }

    /**
     * @hide
     */
+45 −16
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_STOPPED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED;
import static android.hardware.biometrics.BiometricConstants.LockoutMode;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;

import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -61,6 +65,7 @@ import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.CancellationSignal;
@@ -865,10 +870,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        }
    }

    private void handleFingerprintLockoutReset() {
        boolean changed = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
        mFingerprintLockedOut = false;
        mFingerprintLockedOutPermanent = false;
    private void handleFingerprintLockoutReset(@LockoutMode int mode) {
        Log.d(TAG, "handleFingerprintLockoutReset: " + mode);
        final boolean wasLockout = mFingerprintLockedOut;
        final boolean wasLockoutPermanent = mFingerprintLockedOutPermanent;
        mFingerprintLockedOut = (mode == BIOMETRIC_LOCKOUT_TIMED)
                || mode == BIOMETRIC_LOCKOUT_PERMANENT;
        mFingerprintLockedOutPermanent = (mode == BIOMETRIC_LOCKOUT_PERMANENT);
        final boolean changed = (mFingerprintLockedOut != wasLockout)
                || (mFingerprintLockedOutPermanent != wasLockoutPermanent);

        if (isUdfpsEnrolled()) {
            // TODO(b/194825098): update the reset signal(s)
@@ -878,7 +888,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
            // be noticeable.
            mHandler.postDelayed(() -> {
                updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
            }, BIOMETRIC_LOCKOUT_RESET_DELAY_MS);
            }, getBiometricLockoutDelay());
        } else {
            updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
        }
@@ -1076,13 +1086,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        }
    }

    private void handleFaceLockoutReset() {
        boolean changed = mFaceLockedOutPermanent;
        mFaceLockedOutPermanent = false;
    private void handleFaceLockoutReset(@LockoutMode int mode) {
        Log.d(TAG, "handleFaceLockoutReset: " + mode);
        final boolean wasLockoutPermanent = mFaceLockedOutPermanent;
        mFaceLockedOutPermanent = (mode == BIOMETRIC_LOCKOUT_PERMANENT);
        final boolean changed = (mFaceLockedOutPermanent != wasLockoutPermanent);

        mHandler.postDelayed(() -> {
            updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
        }, BIOMETRIC_LOCKOUT_RESET_DELAY_MS);
        }, getBiometricLockoutDelay());

        if (changed) {
            notifyLockedOutStateChanged(BiometricSourceType.FACE);
@@ -1462,7 +1474,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
            = new FingerprintManager.LockoutResetCallback() {
        @Override
        public void onLockoutReset(int sensorId) {
            handleFingerprintLockoutReset();
            handleFingerprintLockoutReset(BIOMETRIC_LOCKOUT_NONE);
        }
    };

@@ -1470,7 +1482,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
            = new FaceManager.LockoutResetCallback() {
        @Override
        public void onLockoutReset(int sensorId) {
            handleFaceLockoutReset();
            handleFaceLockoutReset(BIOMETRIC_LOCKOUT_NONE);
        }
    };

@@ -1580,10 +1592,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                }
    };

    private CancellationSignal mFingerprintCancelSignal;
    private CancellationSignal mFaceCancelSignal;
    @VisibleForTesting
    CancellationSignal mFingerprintCancelSignal;
    @VisibleForTesting
    CancellationSignal mFaceCancelSignal;
    private FingerprintManager mFpm;
    private FaceManager mFaceManager;
    private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
    private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
    private boolean mFingerprintLockedOut;
    private boolean mFingerprintLockedOutPermanent;
@@ -2018,6 +2033,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
            mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
            mFingerprintSensorProperties = mFpm.getSensorPropertiesInternal();
        }
        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
            mFaceManager = (FaceManager) context.getSystemService(Context.FACE_SERVICE);
@@ -2382,11 +2398,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        final boolean shouldListenUdfpsState = !isUdfps
                || (!userCanSkipBouncer
                    && !isEncryptedOrLockdownForUser
                    && userDoesNotHaveTrust
                    && !mFingerprintLockedOut);
                    && userDoesNotHaveTrust);

        final boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
                && shouldListenBouncerState && shouldListenUdfpsState;
                && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut();

        if (DEBUG_FINGERPRINT || DEBUG_SPEW) {
            maybeLogListenerModelData(
@@ -2773,6 +2788,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                cb.onUserSwitchComplete(userId);
            }
        }

        if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) {
            handleFaceLockoutReset(mFaceManager.getLockoutModeForUser(
                    mFaceSensorProperties.get(0).sensorId, userId));
        }
        if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) {
            handleFingerprintLockoutReset(mFpm.getLockoutModeForUser(
                    mFingerprintSensorProperties.get(0).sensorId, userId));
        }

        mInteractionJankMonitor.end(InteractionJankMonitor.CUJ_USER_SWITCH);
        mLatencyTracker.onActionEnd(LatencyTracker.ACTION_USER_SWITCH);
    }
@@ -3508,6 +3533,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        }
    }

    protected int getBiometricLockoutDelay() {
        return BIOMETRIC_LOCKOUT_RESET_DELAY_MS;
    }

    /**
     * Unregister all listeners.
     */
+83 −1
Original line number Diff line number Diff line
@@ -35,10 +35,12 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -50,15 +52,20 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.UserHandle;
@@ -121,6 +128,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
            TEST_CARRIER_ID, 0);
    private static final int FACE_SENSOR_ID = 0;
    private static final int FINGERPRINT_SENSOR_ID = 1;

    @Mock
    private DumpManager mDumpManager;
    @Mock
@@ -212,6 +222,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {

        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
        when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
                new FingerprintSensorPropertiesInternal(1 /* sensorId */,
                        FingerprintSensorProperties.STRENGTH_STRONG,
                        1 /* maxEnrollmentsPerUser */,
                        List.of(new ComponentInfoInternal("fingerprintSensor" /* componentId */,
                                "vendor/model/revision" /* hardwareVersion */,
                                "1.01" /* firmwareVersion */,
                                "00000001" /* serialNumber */, "" /* softwareVersion */)),
                        FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
                        false /* resetLockoutRequiresHAT */)));
        when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
        when(mUserManager.isPrimaryUser()).thenReturn(true);
        when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -232,9 +252,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        mSpiedContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);

        mMockitoSession = ExtendedMockito.mockitoSession()
                .spyStatic(SubscriptionManager.class).startMocking();
                .spyStatic(SubscriptionManager.class)
                .spyStatic(ActivityManager.class)
                .startMocking();
        ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                .when(SubscriptionManager::getDefaultSubscriptionId);
        ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
                .when(ActivityManager::getCurrentUser);

        mTestableLooper = TestableLooper.get(this);
        allowTestableLooperAsMainThread();
@@ -765,6 +789,59 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        verify(mLatencyTracker).onActionEnd(LatencyTracker.ACTION_USER_SWITCH);
    }

    @Test
    public void testMultiUserLockoutChanged_whenUserSwitches() {
        testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT,
                BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT);
    }

    @Test
    public void testMultiUserLockoutNotChanged_whenUserSwitches() {
        testMultiUserLockout_whenUserSwitches(BiometricConstants.BIOMETRIC_LOCKOUT_NONE,
                BiometricConstants.BIOMETRIC_LOCKOUT_NONE);
    }

    private void testMultiUserLockout_whenUserSwitches(
            @BiometricConstants.LockoutMode int fingerprintLockoutMode,
            @BiometricConstants.LockoutMode int faceLockoutMode) {
        final int newUser = 12;
        final boolean faceLocked =
                faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
        final boolean fpLocked =
                fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
        when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
                .thenReturn(fingerprintLockoutMode);
        when(mFaceManager.getLockoutModeForUser(eq(FACE_SENSOR_ID), eq(newUser)))
                .thenReturn(faceLockoutMode);

        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
        mTestableLooper.processAllMessages();
        mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);

        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
                anyInt());

        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
        final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
        mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel;
        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
        mKeyguardUpdateMonitor.registerCallback(callback);

        mKeyguardUpdateMonitor.handleUserSwitchComplete(newUser);
        mTestableLooper.processAllMessages();

        verify(faceCancel, faceLocked ? times(1) : never()).cancel();
        verify(fpCancel, fpLocked ? times(1) : never()).cancel();
        verify(callback, faceLocked ? times(1) : never()).onBiometricRunningStateChanged(
                eq(false), eq(BiometricSourceType.FACE));
        verify(callback, fpLocked ? times(1) : never()).onBiometricRunningStateChanged(
                eq(false), eq(BiometricSourceType.FINGERPRINT));
        assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLocked);
        assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLocked);
    }

    @Test
    public void testGetUserCanSkipBouncer_whenTrust() {
        int user = KeyguardUpdateMonitor.getCurrentUser();
@@ -1124,5 +1201,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
            mSimStateChanged.set(true);
            super.handleSimStateChange(subId, slotId, state);
        }

        @Override
        protected int getBiometricLockoutDelay() {
            return 0;
        }
    }
}
Loading