diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java index 487e254a929..69ae9a735e3 100644 --- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java +++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java @@ -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); diff --git a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java index 2ce0757f77b..4781f5623ae 100644 --- a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java @@ -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 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 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 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 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 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 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"); + } } } }