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

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

Update authentication when encrypted or lockout

Fingerprint authentication should not expose accept/reject/lockout
when the user is encrypted or locked out. This is possible with
IBiometricsFingerprint@2.1 since lockout is controlled by the framework.

IBiometricsFace@1.0 does not support this since lockout is controlled
in the HAL (or lower).

Bug: 79776455

Test: On fingerprint device, during encrypted or lockdown, any finger
      works, lockout never occurs
Test: BiometricPromptDemo, normal path is run (e.g. incorrect fingers
      are rejected)
Test: Test no effect on face device
Test: atest KeyguardUpdateMonitorTest

Change-Id: I9ded8efd80d4f8b92ce054262e721853703c6437
Merged-In: I6c9717d1f8ed3e844b3d92727396e2ce2e7fd94f
parent 7b1c1db8
Loading
Loading
Loading
Loading
+81 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;

import android.annotation.NonNull;
@@ -75,11 +76,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
    private static final int MSG_ERROR = 104;
    private static final int MSG_REMOVED = 105;
    private static final int MSG_ENUMERATED = 106;
    private static final int MSG_FINGERPRINT_DETECTED = 107;

    private IFingerprintService mService;
    private Context mContext;
    private IBinder mToken = new Binder();
    private AuthenticationCallback mAuthenticationCallback;
    private FingerprintDetectionCallback mFingerprintDetectionCallback;
    private EnrollmentCallback mEnrollmentCallback;
    private RemovalCallback mRemovalCallback;
    private EnumerateCallback mEnumerateCallback;
@@ -107,6 +110,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
        }
    }

    private class OnFingerprintDetectionCancelListener implements OnCancelListener {
        @Override
        public void onCancel() {
            cancelFingerprintDetect();
        }
    }

    /**
     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
     * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
@@ -271,6 +281,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
        public void onAuthenticationAcquired(int acquireInfo) {}
    };

    /**
     * Callback structure provided for {@link #detectFingerprint(CancellationSignal,
     * FingerprintDetectionCallback, int)}.
     * @hide
     */
    public interface FingerprintDetectionCallback {
        /**
         * Invoked when a fingerprint has been detected.
         */
        void onFingerprintDetected(int userId, boolean isStrongBiometric);
    }

    /**
     * Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
     * int, int, EnrollmentCallback)} must provide an implementation of this for listening to
@@ -453,6 +475,35 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
        }
    }

    /**
     * Uses the fingerprint hardware to detect for the presence of a finger, without giving details
     * about accept/reject/lockout.
     * @hide
     */
    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
    public void detectFingerprint(@NonNull CancellationSignal cancel,
            @NonNull FingerprintDetectionCallback callback, int userId) {
        if (mService == null) {
            return;
        }

        if (cancel.isCanceled()) {
            Slog.w(TAG, "Detection already cancelled");
            return;
        } else {
            cancel.setOnCancelListener(new OnFingerprintDetectionCancelListener());
        }

        mFingerprintDetectionCallback = callback;

        try {
            mService.detectFingerprint(mToken, userId, mServiceReceiver,
                    mContext.getOpPackageName());
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception when requesting finger detect", e);
        }
    }

    /**
     * Request fingerprint enrollment. This call warms up the fingerprint hardware
     * and starts scanning for fingerprints. Progress will be indicated by callbacks to the
@@ -797,6 +848,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
                    sendEnumeratedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
                            msg.arg2 /* groupId */);
                    break;
                case MSG_FINGERPRINT_DETECTED:
                    sendFingerprintDetected(msg.arg1 /* userId */,
                            (boolean) msg.obj /* isStrongBiometric */);
                    break;
            }
        }
    };
@@ -891,6 +946,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
        }
    }

    private void sendFingerprintDetected(int userId, boolean isStrongBiometric) {
        if (mFingerprintDetectionCallback == null) {
            Slog.e(TAG, "sendFingerprintDetected, callback null");
            return;
        }
        mFingerprintDetectionCallback.onFingerprintDetected(userId, isStrongBiometric);
    }

    /**
     * @hide
     */
@@ -927,6 +990,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
        }
    }

    private void cancelFingerprintDetect() {
        if (mService == null) {
            return;
        }

        try {
            mService.cancelFingerprintDetect(mToken, mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     */
@@ -1032,6 +1107,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
                    fp).sendToTarget();
        }

        @Override
        public void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric) {
            mHandler.obtainMessage(MSG_FINGERPRINT_DETECTED, userId, 0, isStrongBiometric)
                    .sendToTarget();
        }

        @Override // binder call
        public void onAuthenticationFailed(long deviceId) {
            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+8 −0
Original line number Diff line number Diff line
@@ -33,6 +33,11 @@ interface IFingerprintService {
    void authenticate(IBinder token, long sessionId, int userId,
            IFingerprintServiceReceiver receiver, int flags, String opPackageName);

    // Uses the fingerprint hardware to detect for the presence of a finger, without giving details
    // about accept/reject/lockout.
    void detectFingerprint(IBinder token, int userId, IFingerprintServiceReceiver receiver,
            String opPackageName);

    // This method prepares the service to start authenticating, but doesn't start authentication.
    // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
    // called from BiometricService. The additional uid, pid, userId arguments should be determined
@@ -48,6 +53,9 @@ interface IFingerprintService {
    // Cancel authentication for the given sessionId
    void cancelAuthentication(IBinder token, String opPackageName);

    // Cancel finger detection
    void cancelFingerprintDetect(IBinder token, String opPackageName);

    // Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
    // an additional uid, pid, userid.
    void cancelAuthenticationFromService(IBinder token, String opPackageName,
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ oneway interface IFingerprintServiceReceiver {
    void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
    void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId,
            boolean isStrongBiometric);
    void onFingerprintDetected(long deviceId, int userId, boolean isStrongBiometric);
    void onAuthenticationFailed(long deviceId);
    void onError(long deviceId, int error, int vendorCode);
    void onRemoved(long deviceId, int fingerId, int groupId, int remaining);
+23 −3
Original line number Diff line number Diff line
@@ -1072,6 +1072,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
    }

    private boolean isEncryptedOrLockdown(int userId) {
        final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
        final boolean isLockDown =
                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
        return isEncrypted || isLockDown;
    }

    public boolean userNeedsStrongAuth() {
        return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
                != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -1248,6 +1257,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        }
    };

    // Trigger the fingerprint success path so the bouncer can be shown
    private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback
            = this::handleFingerprintAuthenticated;

    private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
            = new AuthenticationCallback() {

@@ -2050,8 +2063,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                mFingerprintCancelSignal.cancel();
            }
            mFingerprintCancelSignal = new CancellationSignal();
            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
                    null, userId);

            if (isEncryptedOrLockdown(userId)) {
                mFpm.detectFingerprint(mFingerprintCancelSignal, mFingerprintDetectionCallback,
                        userId);
            } else {
                mFpm.authenticate(null, mFingerprintCancelSignal, 0,
                        mFingerprintAuthenticationCallback, null, userId);
            }

            setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
        }
    }
@@ -2087,7 +2107,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

    private boolean isUnlockWithFingerprintPossible(int userId) {
        return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
                && mFpm.getEnrolledFingerprints(userId).size() > 0;
                && mFpm.hasEnrolledTemplates(userId);
    }

    private boolean isUnlockWithFacePossible(int userId) {
+39 −0
Original line number Diff line number Diff line
@@ -174,6 +174,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        when(mFaceManager.isHardwareDetected()).thenReturn(true);
        when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
        when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
        when(mUserManager.isPrimaryUser()).thenReturn(true);
        when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
@@ -418,6 +420,43 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
    }

    @Test
    public void testTriesToAuthenticateFingerprint_whenKeyguard() {
        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
        mTestableLooper.processAllMessages();

        verify(mFingerprintManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
        verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
    }

    @Test
    public void testFingerprintDoesNotAuth_whenEncrypted() {
        testFingerprintWhenStrongAuth(
                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
    }

    @Test
    public void testFingerprintDoesNotAuth_whenDpmLocked() {
        testFingerprintWhenStrongAuth(
                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW);
    }

    @Test
    public void testFingerprintDoesNotAuth_whenUserLockdown() {
        testFingerprintWhenStrongAuth(
                KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
    }

    private void testFingerprintWhenStrongAuth(int strongAuth) {
        when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
        mTestableLooper.processAllMessages();

        verify(mFingerprintManager, never())
                .authenticate(any(), any(), anyInt(), any(), any(), anyInt());
        verify(mFingerprintManager).detectFingerprint(any(), any(), anyInt());
    }

    @Test
    public void testTriesToAuthenticate_whenBouncer() {
        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
Loading