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

Commit 571124ed authored by Joe Bolinger's avatar Joe Bolinger
Browse files

Add modality switch callback to update UDFPS layout during configuration changes.

Addresses a race condition when the view is attached and the modality switch is in progress.

Also fixes an issue with the confirmation button being re-enabled after a configuration change.

Bug: 190832486
Test: atest AuthBiometricFaceToFingerprintViewTest
Change-Id: I701d9d6f03474df2dda6f48e506208ddf7eab2c3
parent 43df5b95
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
    }

    @Modality private int mActiveSensorType = TYPE_FACE;
    @Nullable private ModalityListener mModalityListener;
    @Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps;
    @Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;

@@ -115,6 +116,10 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
        return mFingerprintSensorProps.isAnyUdfpsType();
    }

    void setModalityListener(@NonNull ModalityListener listener) {
        mModalityListener = listener;
    }

    void setFingerprintSensorProps(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
        mFingerprintSensorProps = sensorProps;
    }
@@ -182,11 +187,16 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
    @Override
    public void updateState(@BiometricState int newState) {
        if (mState == STATE_HELP || mState == STATE_ERROR) {
            @Modality final int currentType = mActiveSensorType;
            mActiveSensorType = TYPE_FINGERPRINT;

            setRequireConfirmation(false);
            mConfirmButton.setEnabled(false);
            mConfirmButton.setVisibility(View.GONE);

            if (mModalityListener != null && currentType != mActiveSensorType) {
                mModalityListener.onModalitySwitched(currentType, mActiveSensorType);
            }
        }

        super.updateState(newState);
+3 −0
Original line number Diff line number Diff line
@@ -759,6 +759,9 @@ public abstract class AuthBiometricView extends LinearLayout {
            // Restore positive button(s) state
            mConfirmButton.setVisibility(
                    mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_CONFIRM_VISIBILITY));
            if (mConfirmButton.getVisibility() == View.GONE) {
                setRequireConfirmation(false);
            }
            mTryAgainButton.setVisibility(
                    mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));

+23 −16
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.PromptInfo;
@@ -356,6 +355,12 @@ public class AuthContainerView extends LinearLayout
                            (AuthBiometricFaceToFingerprintView) factory.inflate(
                                    R.layout.auth_biometric_face_to_fingerprint_view, null, false);
                    faceToFingerprintView.setFingerprintSensorProps(fingerprintSensorProps);
                    faceToFingerprintView.setModalityListener(new ModalityListener() {
                        @Override
                        public void onModalitySwitched(int oldModality, int newModality) {
                            maybeUpdatePositionForUdfps(true /* invalidate */);
                        }
                    });
                    mBiometricView = faceToFingerprintView;
                } else {
                    Log.e(TAG, "Fingerprint props not found for sensor ID: " + fingerprintSensorId);
@@ -489,9 +494,7 @@ public class AuthContainerView extends LinearLayout
                    + mConfig.mPromptInfo.getAuthenticators());
        }

        if (shouldUpdatePositionForUdfps()) {
            updatePositionForUdfps();
        }
        maybeUpdatePositionForUdfps(false /* invalidate */);

        if (mConfig.mSkipIntro) {
            mContainerState = STATE_SHOWING;
@@ -536,14 +539,14 @@ public class AuthContainerView extends LinearLayout
        }
    }

    private boolean shouldUpdatePositionForUdfps() {
        if (mBiometricView instanceof AuthBiometricUdfpsView) {
    private static boolean shouldUpdatePositionForUdfps(@NonNull View view) {
        if (view instanceof AuthBiometricUdfpsView) {
            return true;
        }

        if (mBiometricView instanceof AuthBiometricFaceToFingerprintView) {
        if (view instanceof AuthBiometricFaceToFingerprintView) {
            AuthBiometricFaceToFingerprintView faceToFingerprintView =
                    (AuthBiometricFaceToFingerprintView) mBiometricView;
                    (AuthBiometricFaceToFingerprintView) view;
            return faceToFingerprintView.getActiveSensorType() == TYPE_FINGERPRINT
                    && faceToFingerprintView.isFingerprintUdfps();
        }
@@ -551,7 +554,11 @@ public class AuthContainerView extends LinearLayout
        return false;
    }

    private void updatePositionForUdfps() {
    private boolean maybeUpdatePositionForUdfps(boolean invalidate) {
        if (!shouldUpdatePositionForUdfps(mBiometricView)) {
            return false;
        }

        final int displayRotation = getDisplay().getRotation();
        switch (displayRotation) {
            case Surface.ROTATION_0:
@@ -576,6 +583,13 @@ public class AuthContainerView extends LinearLayout
                setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
                break;
        }

        if (invalidate) {
            mPanelView.invalidateOutline();
            mBiometricView.requestLayout();
        }

        return true;
    }

    private void setScrollViewGravity(int gravity) {
@@ -626,13 +640,6 @@ public class AuthContainerView extends LinearLayout
    @Override
    public void onAuthenticationFailed(@Modality int modality, String failureReason) {
        mBiometricView.onAuthenticationFailed(modality, failureReason);
        if (mBiometricView instanceof AuthBiometricFaceToFingerprintView
                && ((AuthBiometricFaceToFingerprintView) mBiometricView).isFingerprintUdfps()
                && modality == BiometricAuthenticator.TYPE_FACE) {
            updatePositionForUdfps();
            mPanelView.invalidateOutline();
            mBiometricView.requestLayout();
        }
    }

    @Override
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.biometrics;

import android.hardware.biometrics.BiometricAuthenticator.Modality;

/**
 * Listener for events related to modality changes during operations.
 *
 * Used by views such as {@link AuthBiometricFaceToFingerprintView} that support fallback style
 * authentication.
 */
public interface ModalityListener {

    /**
     * The modality has changed. Called after the transition has been fully completed.
     *
     * @param oldModality original modality
     * @param newModality current modality
     */
    default void onModalitySwitched(@Modality int oldModality, @Modality int newModality) {}
}
+25 −0
Original line number Diff line number Diff line
@@ -137,6 +137,31 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
        verify(mConfirmButton).setVisibility(eq(View.GONE));
    }

    @Test
    public void testStateUpdated_whenSwitchToFingerprint_invokesCallbacks() {
        class TestModalityListener implements ModalityListener {
            public int switchCount = 0;

            @Override
            public void onModalitySwitched(int oldModality, int newModality) {
                assertEquals(TYPE_FINGERPRINT, newModality);
                assertEquals(TYPE_FACE, oldModality);
                switchCount++;
            }
        }
        final TestModalityListener modalityListener = new TestModalityListener();

        mFaceToFpView.onDialogAnimatedIn();
        mFaceToFpView.setModalityListener(modalityListener);

        assertEquals(0, modalityListener.switchCount);

        mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
        mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);

        assertEquals(1, modalityListener.switchCount);
    }

    @Test
    public void testModeUpdated_onSoftError_whenSwitchToFingerprint() {
        mFaceToFpView.onDialogAnimatedIn();