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

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

3/n: Move generateChallenge/resetLockout/revokeChallenge off critical path

1) Introduces BiometricDeferredQueue in LockSettings package to move
   slow operations off of the critical unlock path.

2) Changes generateChallenge to require sensorId. Callers to
   generateChallengeBlocking are currently not affected. Their path
   will need to be updated in the future.

3) Adds resetLockoutRequiresHardwareAuthToken for fingerprint sensor
   properties, since IBiometricsFingerprint@2.1 and its derivatives
   do not require the HAT yet

Fixes: 145978626

Test: atest com.android.server.biometrics
Test: Able to enroll after entering password
Test: Reset lockout on face device with single-profile-per-user
Test: Reset lockout on face device with managed profile + unified
      credential
Test: Reset lockout on face device with managed profile and separate
      credential (both owner and managed profile)
Test: Reset lockout for secondary user

Change-Id: Id4d7c39274a52ef61709161b6f24ec4f5d76720e
parent 509f1c79
Loading
Loading
Loading
Loading
+0 −20
Original line number Diff line number Diff line
@@ -299,26 +299,6 @@ public class BiometricManager {
        }
    }

    /**
     * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
     *
     * @param userId this operation takes effect for.
     * @param hardwareAuthToken an opaque token returned by password confirmation.
     * @hide
     */
    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
    public void resetLockout(int userId, byte[] hardwareAuthToken) {
        if (mService != null) {
            try {
                mService.resetLockout(userId, hardwareAuthToken);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } else {
            Slog.w(TAG, "resetLockout(): Service not connected");
        }
    }

    /**
     * Get a list of AuthenticatorIDs for biometric authenticators which have 1) enrolled templates,
     * and 2) meet the requirements for integrating with Keystore. The AuthenticatorIDs are known
+0 −3
Original line number Diff line number Diff line
@@ -46,9 +46,6 @@ interface IAuthService {
    // Register callback for when keyguard biometric eligibility changes.
    void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);

    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
    void resetLockout(int userId, in byte [] hardwareAuthToken);

    // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet
    // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
    // land as SIDs, and are used during key generation.
+0 −3
Original line number Diff line number Diff line
@@ -53,9 +53,6 @@ interface IBiometricAuthenticator {
    // Return the LockoutTracker status for the specified user
    int getLockoutModeForUser(int userId);

    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
    void resetLockout(int userId, in byte [] hardwareAuthToken);

    // Gets the authenticator ID representing the current set of enrolled templates
    long getAuthenticatorId(int callingUserId);
}
+0 −3
Original line number Diff line number Diff line
@@ -56,9 +56,6 @@ interface IBiometricService {
    // Client lifecycle is still managed in <Biometric>Service.
    void onReadyForAuthentication(int cookie);

    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
    void resetLockout(int userId, in byte [] hardwareAuthToken);

    // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet
    // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
    // land as SIDs, and are used during key generation.
+65 −50
Original line number Diff line number Diff line
@@ -143,14 +143,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
        }

        @Override
        public void onChallengeGenerated(long challenge) {
            if (mGenerateChallengeCallback instanceof InternalGenerateChallengeCallback) {
                // Perform this on system_server thread, since the application's thread is
                // blocked waiting for the result
                mGenerateChallengeCallback.onGenerateChallengeResult(challenge);
            } else {
                mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, challenge).sendToTarget();
            }
        public void onChallengeGenerated(int sensorId, long challenge) {
            mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
                    .sendToTarget();
        }

        @Override
@@ -416,35 +411,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
        }
    }

    /**
     * Same as {@link #generateChallenge(GenerateChallengeCallback)}, except blocks until the
     * TEE/hardware operation is complete.
     * @return challenge generated in the TEE/hardware
     * @hide
     */
    @RequiresPermission(MANAGE_BIOMETRIC)
    public long generateChallengeBlocking() {
        final AtomicReference<Long> result = new AtomicReference<>();
        final CountDownLatch latch = new CountDownLatch(1);
        final GenerateChallengeCallback callback = new InternalGenerateChallengeCallback() {
            @Override
            public void onGenerateChallengeResult(long challenge) {
                result.set(challenge);
                latch.countDown();
            }
        };

        generateChallenge(callback);

        try {
            latch.await(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Slog.e(TAG, "Interrupted while generatingChallenge", e);
            e.printStackTrace();
        }
        return result.get();
    }

    /**
     * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
     * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
@@ -458,11 +424,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
     * @hide
     */
    @RequiresPermission(MANAGE_BIOMETRIC)
    public void generateChallenge(GenerateChallengeCallback callback) {
    public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
        if (mService != null) {
            try {
                mGenerateChallengeCallback = callback;
                mService.generateChallenge(mToken, mServiceReceiver, mContext.getOpPackageName());
                mService.generateChallenge(mToken, sensorId, mServiceReceiver,
                        mContext.getOpPackageName());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -470,15 +437,66 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
    }

    /**
     * Invalidates the current auth token.
     * Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
     * enumerated sensor.
     * @hide
     */
    @RequiresPermission(MANAGE_BIOMETRIC)
    public void generateChallenge(GenerateChallengeCallback callback) {
        final List<FaceSensorProperties> faceSensorProperties = getSensorProperties();
        if (faceSensorProperties.isEmpty()) {
            Slog.e(TAG, "No sensors");
            return;
        }

        final int sensorId = faceSensorProperties.get(0).sensorId;
        generateChallenge(sensorId, callback);
    }

    /**
     * Invalidates the current challenge.
     *
     * @hide
     */
    @RequiresPermission(MANAGE_BIOMETRIC)
    public void revokeChallenge() {
        final List<FaceSensorProperties> faceSensorProperties = getSensorProperties();
        if (faceSensorProperties.isEmpty()) {
            Slog.e(TAG, "No sensors during revokeChallenge");
        }
        revokeChallenge(faceSensorProperties.get(0).sensorId);
    }

    /**
     * Invalidates the current challenge.
     *
     * @hide
     */
    @RequiresPermission(MANAGE_BIOMETRIC)
    public void revokeChallenge(int sensorId) {
        if (mService != null) {
            try {
                mService.revokeChallenge(mToken, mContext.getOpPackageName());
                mService.revokeChallenge(mToken, sensorId, mContext.getOpPackageName());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
     *
     * @param sensorId Sensor ID that this operation takes effect for
     * @param userId User ID that this operation takes effect for.
     * @param hardwareAuthToken An opaque token returned by password confirmation.
     * @hide
     */
    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
    public void resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
        if (mService != null) {
            try {
                mService.resetLockout(mToken, sensorId, userId, hardwareAuthToken,
                        mContext.getOpPackageName());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -1083,18 +1101,18 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
    }

    /**
     * Callback structure provided to {@link #generateChallenge(GenerateChallengeCallback)}.
     * Callback structure provided to {@link #generateChallenge(int, GenerateChallengeCallback)}.
     * @hide
     */
    public interface GenerateChallengeCallback {
        /**
         * Invoked when a challenge has been generated.
         */
        void onGenerateChallengeResult(long challenge);
        void onGenerateChallengeResult(int sensorId, long challenge);

        /**
         * Invoked if the challenge has not been revoked and a subsequent caller/owner invokes
         * {@link #generateChallenge(GenerateChallengeCallback)}, but
         * {@link #generateChallenge(int, GenerateChallengeCallback)}, but
         */
        default void onChallengeInterrupted(int sensorId) {}

@@ -1104,9 +1122,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
        default void onChallengeInterruptFinished(int sensorId) {}
    }

    private abstract static class InternalGenerateChallengeCallback
            implements GenerateChallengeCallback {}

    private class OnEnrollCancelListener implements OnCancelListener {
        @Override
        public void onCancel() {
@@ -1178,7 +1193,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
                    args.recycle();
                    break;
                case MSG_CHALLENGE_GENERATED:
                    sendChallengeGenerated((long) msg.obj /* challenge */);
                    sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
                    break;
                case MSG_FACE_DETECTED:
                    sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
@@ -1211,11 +1226,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
        mGetFeatureCallback.onCompleted(success, feature, value);
    }

    private void sendChallengeGenerated(long challenge) {
    private void sendChallengeGenerated(int sensorId, long challenge) {
        if (mGenerateChallengeCallback == null) {
            return;
        }
        mGenerateChallengeCallback.onGenerateChallengeResult(challenge);
        mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, challenge);
    }

    private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
Loading