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

Commit 43256017 authored by Milton Wu's avatar Milton Wu
Browse files

Fix crash after confirmLock

Stop everything in generateChallenge() on "Face and Fingerprint Unlock"
page if activity is null or finishing

Also fix ignored test cases in CombinedBiometricProfileSettingsTest

Bug: 292125215
Test: RunSettingsRoboTests ROBOTEST_FILTER=CombinedBiometricProfileSettingsTest
Change-Id: I7a272e813bfddd5d066417a74cbd63d22f511000
parent 9635d1cf
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRIN

import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.biometrics.SensorProperties;
@@ -179,6 +180,12 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
            }

            mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
                final Activity activity = getActivity();
                if (activity == null || activity.isFinishing()) {
                    Log.e(getLogTag(), "Stop during generating face unlock challenge"
                            + " because activity is null or finishing");
                    return;
                }
                try {
                    final byte[] token = requestGatekeeperHat(context, mGkPwHandle, mUserId,
                            challenge);
@@ -215,6 +222,12 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
            }

            mFingerprintManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
                final Activity activity = getActivity();
                if (activity == null || activity.isFinishing()) {
                    Log.e(getLogTag(), "Stop during generating fingerprint challenge"
                            + " because activity is null or finishing");
                    return;
                }
                try {
                    final byte[] token = requestGatekeeperHat(context, mGkPwHandle, mUserId,
                            challenge);
+112 −13
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.util.AndroidRuntimeException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -58,7 +59,10 @@ import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;

import com.android.settings.R;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowFragment;
@@ -68,7 +72,6 @@ import com.android.settingslib.core.AbstractPreferenceController;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -86,7 +89,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Ignore
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class})
public class CombinedBiometricProfileSettingsTest {
@@ -104,6 +106,10 @@ public class CombinedBiometricProfileSettingsTest {
    @Mock
    private BiometricSettingsAppPreferenceController mBiometricSettingsAppPreferenceController;
    @Mock
    private FingerprintStatusPreferenceController mFingerprintStatusPreferenceController;
    @Mock
    private FaceStatusPreferenceController mFaceStatusPreferenceController;
    @Mock
    private FaceManager mFaceManager;
    @Mock
    private FragmentTransaction mFragmentTransaction;
@@ -128,6 +134,29 @@ public class CombinedBiometricProfileSettingsTest {
        List<AbstractPreferenceController> controllerList = new ArrayList<>();
        controllerList.add(mBiometricSettingsAppPreferenceController);
        preferenceControllers.put(BiometricSettingsAppPreferenceController.class, controllerList);
        controllerList.add(mFingerprintStatusPreferenceController);
        preferenceControllers.put(FingerprintStatusPreferenceController.class, controllerList);
        controllerList.add(mFaceStatusPreferenceController);
        preferenceControllers.put(FaceStatusPreferenceController.class, controllerList);

        doAnswer(invocation -> {
            final Preference preference = invocation.getArgument(0);
            return preference.getKey().equals(mFragment.getFingerprintPreferenceKey());
        }).when(mFingerprintStatusPreferenceController)
                .setPreferenceTreeClickLauncher(any(), any());
        doAnswer(invocation -> {
            final Preference preference = invocation.getArgument(0);
            return preference.getKey().equals(mFragment.getFingerprintPreferenceKey());
        }).when(mFingerprintStatusPreferenceController).handlePreferenceTreeClick(any());
        doAnswer(invocation -> {
            final Preference preference = invocation.getArgument(0);
            return preference.getKey().equals(mFragment.getFacePreferenceKey());
        }).when(mFaceStatusPreferenceController)
                .setPreferenceTreeClickLauncher(any(), any());
        doAnswer(invocation -> {
            final Preference preference = invocation.getArgument(0);
            return preference.getKey().equals(mFragment.getFacePreferenceKey());
        }).when(mFaceStatusPreferenceController).handlePreferenceTreeClick(any());

        doAnswer(invocation -> {
            final CharSequence key = invocation.getArgument(0);
@@ -164,7 +193,7 @@ public class CombinedBiometricProfileSettingsTest {
        preference.setKey(mFragment.getFingerprintPreferenceKey());
        mFragment.onPreferenceTreeClick(preference);

        verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
        verify(mFingerprintStatusPreferenceController).handlePreferenceTreeClick(
                mPreferenceCaptor.capture());
        List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();

@@ -224,7 +253,7 @@ public class CombinedBiometricProfileSettingsTest {
        mFragment.onActivityResult(CONFIRM_REQUEST, RESULT_FINISHED,
                new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));

        verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
        verify(mFingerprintStatusPreferenceController).handlePreferenceTreeClick(
                mPreferenceCaptor.capture());
        List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
        assertThat(capturedPreferences.size()).isEqualTo(1);
@@ -254,7 +283,7 @@ public class CombinedBiometricProfileSettingsTest {
        preference.setKey(mFragment.getFacePreferenceKey());
        mFragment.onPreferenceTreeClick(preference);

        verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
        verify(mFaceStatusPreferenceController).handlePreferenceTreeClick(
                mPreferenceCaptor.capture());
        List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
        assertThat(capturedPreferences.size()).isEqualTo(1);
@@ -313,7 +342,7 @@ public class CombinedBiometricProfileSettingsTest {
        preference.setKey(mFragment.getFacePreferenceKey());
        mFragment.onPreferenceTreeClick(preference);

        verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
        verify(mFaceStatusPreferenceController).handlePreferenceTreeClick(
                mPreferenceCaptor.capture());
        List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
        assertThat(capturedPreferences.size()).isEqualTo(1);
@@ -323,7 +352,7 @@ public class CombinedBiometricProfileSettingsTest {
    @Test
    public void testClickFingerprintUnlock_inMultiWindow_withoutEnrolledFp_showsDialog() {
        testClickFingerprintUnlock(true /* isInMultiWindow */, false /* hasEnrolledFingerprint */);
        verifyShowsDialogAfterClickingUnlock();
        verifyShowsDialogAfterClickingUnlock(mFragment.getFingerprintPreferenceKey());
    }

    @Test
@@ -380,7 +409,7 @@ public class CombinedBiometricProfileSettingsTest {
    @Test
    public void testClickFaceUnlock_inMultiWindow_withoutEnrolledFp_showsDialog() {
        testClickFaceUnlock(true /* isInMultiWindow */, false /*hasEnrolledFace*/);
        verifyShowsDialogAfterClickingUnlock();
        verifyShowsDialogAfterClickingUnlock(mFragment.getFacePreferenceKey());
    }

    @Test
@@ -424,8 +453,11 @@ public class CombinedBiometricProfileSettingsTest {
    }

    private void verifyNoDialogAfterClickingUnlock(String preferenceKey) {
        verify(mBiometricSettingsAppPreferenceController).handlePreferenceTreeClick(
                mPreferenceCaptor.capture());
        final BiometricStatusPreferenceController controller =
                preferenceKey.equals(mFragment.getFacePreferenceKey())
                        ? mFaceStatusPreferenceController
                        : mFingerprintStatusPreferenceController;
        verify(controller).handlePreferenceTreeClick(mPreferenceCaptor.capture());
        List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
        assertThat(capturedPreferences).hasSize(1);
        assertThat(capturedPreferences.get(0).getKey()).isEqualTo(preferenceKey);
@@ -433,12 +465,77 @@ public class CombinedBiometricProfileSettingsTest {
                eq(BiometricsSplitScreenDialog.class.getName()));
    }

    private void verifyShowsDialogAfterClickingUnlock() {
        verify(mBiometricSettingsAppPreferenceController, never()).handlePreferenceTreeClick(any());
    private void verifyShowsDialogAfterClickingUnlock(String preferenceKey) {
        final BiometricStatusPreferenceController controller =
                preferenceKey.equals(mFragment.getFacePreferenceKey())
                        ? mFaceStatusPreferenceController
                        : mFingerprintStatusPreferenceController;
        verify(controller, never()).handlePreferenceTreeClick(any());
        verify(mFragmentTransaction).add(any(),
                eq(BiometricsSplitScreenDialog.class.getName()));
    }

    @Test
    public void testNoCrashIfDetachActivityDuringGeneratingChallengeThroughFaceManager() {
        doAnswer(invocation -> {
            final FaceManager.GenerateChallengeCallback callback =
                    invocation.getArgument(1);
            mFragment.onPause();
            mFragment.onStop();
            mFragment.onDestroy();
            mFragment.onDetach();
            doReturn(null).when(mFragment).getActivity();
            callback.onGenerateChallengeResult(0, 0, 1L);
            return null;
        }).when(mFaceManager).generateChallenge(anyInt(), any());
        doThrow(new IllegalStateException("Test")).when(mFragment).requestGatekeeperHat(
                any(), anyLong(), anyInt(), anyLong());
        FragmentManager fragmentManager = mock(FragmentManager.class);

        // Start fragment
        mFragment.onAttach(mContext);
        mFragment.onCreate(null);
        mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
        mFragment.onResume();

        // User clicks on "Face Unlock"
        final Preference preference = new Preference(mContext);
        preference.setKey(mFragment.getFacePreferenceKey());
        mFragment.onPreferenceTreeClick(preference);

        verify(mFragment, never()).launchChooseOrConfirmLock();
    }

    @Test
    public void testNoCrashIfDetachActivityDuringGeneratingChallengeThroughFingerprintManager() {
        doAnswer(invocation -> {
            final FingerprintManager.GenerateChallengeCallback callback =
                    invocation.getArgument(1);
            mFragment.onPause();
            mFragment.onStop();
            mFragment.onDestroy();
            mFragment.onDetach();
            doReturn(null).when(mFragment).getActivity();
            callback.onChallengeGenerated(0, 0, 1L);
            return null;
        }).when(mFingerprintManager).generateChallenge(anyInt(), any());
        doThrow(new IllegalStateException("Test")).when(mFragment).requestGatekeeperHat(
                any(), anyLong(), anyInt(), anyLong());

        // Start fragment
        mFragment.onAttach(mContext);
        mFragment.onCreate(null);
        mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
        mFragment.onResume();

        // User clicks on "Fingerprint Unlock"
        final Preference preference = new Preference(mContext);
        preference.setKey(mFragment.getFingerprintPreferenceKey());
        mFragment.onPreferenceTreeClick(preference);

        verify(mFragment, never()).launchChooseOrConfirmLock();
    }

    /**
     * a test fragment that initializes PreferenceScreen for testing.
     */
@@ -492,7 +589,9 @@ public class CombinedBiometricProfileSettingsTest {

        @Override
        protected void launchChooseOrConfirmLock() {
            // do nothing
            if (getActivity() == null) {
                throw new AndroidRuntimeException("TestFailed");
            }
        }
    }
}