diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java index caa7327394a..b9a0b939a6b 100644 --- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java +++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java @@ -65,7 +65,8 @@ public abstract class BiometricsSettingsBase extends DashboardFragment { static final int CONFIRM_REQUEST = 2001; private static final int CHOOSE_LOCK_REQUEST = 2002; protected static final int ACTIVE_UNLOCK_REQUEST = 2003; - private static final int BIOMETRIC_AUTH_REQUEST = 2004; + @VisibleForTesting + static final int BIOMETRIC_AUTH_REQUEST = 2004; private static final String SAVE_STATE_CONFIRM_CREDETIAL = "confirm_credential"; private static final String DO_NOT_FINISH_ACTIVITY = "do_not_finish_activity"; diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index 4c18309384c..915fe179402 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -160,7 +160,8 @@ public class ChooseLockGeneric extends SettingsActivity { static final int CHOOSE_LOCK_BEFORE_BIOMETRIC_REQUEST = 103; @VisibleForTesting static final int SKIP_FINGERPRINT_REQUEST = 104; - private static final int BIOMETRIC_AUTH_REQUEST = 105; + @VisibleForTesting + static final int BIOMETRIC_AUTH_REQUEST = 105; private LockPatternUtils mLockPatternUtils; private DevicePolicyManager mDpm; 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 8593860e5aa..a775731df59 100644 --- a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricProfileSettingsTest.java @@ -24,7 +24,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -36,9 +38,12 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.Flags; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.AndroidRuntimeException; import android.view.LayoutInflater; import android.view.View; @@ -57,6 +62,7 @@ import com.android.settings.R; import com.android.settings.biometrics.face.FaceStatusPreferenceController; import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController; import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settings.password.ConfirmDeviceCredentialActivity; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment; @@ -92,6 +98,8 @@ public class CombinedBiometricProfileSettingsTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Captor private ArgumentCaptor mPreferenceCaptor; @Mock @@ -168,6 +176,26 @@ public class CombinedBiometricProfileSettingsTest { ShadowUtils.reset(); } + @Test + @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS) + public void testLaunchBiometricPrompt_onCreateFragment() { + ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + doNothing().when(mFragment).startActivityForResult(any(), anyInt()); + when(mBiometricManager.canAuthenticate( + BiometricManager.Authenticators.MANDATORY_BIOMETRICS)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + + mFragment.onAttach(mContext); + mFragment.onCreate(null); + + verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(), + eq(BiometricsSettingsBase.BIOMETRIC_AUTH_REQUEST)); + + Intent intent = intentArgumentCaptor.getValue(); + assertThat(intent.getComponent().getClassName()).isEqualTo( + ConfirmDeviceCredentialActivity.class.getName()); + } + @Test public void testClickFingerprintUnlockWithValidGkPwHandle() { doAnswer(invocation -> { diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java index 58e7e2d4003..4ef32237086 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java @@ -19,6 +19,7 @@ package com.android.settings.biometrics.fingerprint; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL; +import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST; import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment; import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.CHOOSE_LOCK_GENERIC_REQUEST; import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_REQUIRE_SCREEN_ON_TO_AUTH; @@ -34,11 +35,14 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.ComponentInfoInternal; +import android.hardware.biometrics.Flags; import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; @@ -46,6 +50,8 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.os.CancellationSignal; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -56,6 +62,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.test.core.app.ApplicationProvider; import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settings.password.ConfirmDeviceCredentialActivity; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; @@ -85,6 +92,11 @@ import java.util.ArrayList; @Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class, ShadowUserManager.class, ShadowLockPatternUtils.class}) public class FingerprintSettingsFragmentTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final int PRIMARY_USER_ID = 0; private static final int GUEST_USER_ID = 10; @@ -92,12 +104,12 @@ public class FingerprintSettingsFragmentTest { private Context mContext; private FragmentActivity mActivity; - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock private FingerprintManager mFingerprintManager; @Mock private FragmentTransaction mFragmentTransaction; + @Mock + private BiometricManager mBiometricManager; @Captor private ArgumentCaptor mCancellationSignalArgumentCaptor = @@ -117,8 +129,11 @@ public class FingerprintSettingsFragmentTest { mContext = spy(ApplicationProvider.getApplicationContext()); mFragment = spy(new FingerprintSettingsFragment()); doReturn(mContext).when(mFragment).getContext(); - + doReturn(mBiometricManager).when(mContext).getSystemService(BiometricManager.class); doReturn(true).when(mFingerprintManager).isHardwareDetected(); + when(mBiometricManager.canAuthenticate( + BiometricManager.Authenticators.MANDATORY_BIOMETRICS)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE); } @After @@ -139,6 +154,25 @@ public class FingerprintSettingsFragmentTest { false)).isTrue(); } + @Test + @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS) + public void testLaunchBiometricPromptForFingerprint() { + when(mBiometricManager.canAuthenticate( + BiometricManager.Authenticators.MANDATORY_BIOMETRICS)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + + setUpFragment(false); + ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass( + Intent.class); + + verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(), + eq(BIOMETRIC_AUTH_REQUEST)); + + Intent intent = intentArgumentCaptor.getValue(); + assertThat(intent.getComponent().getClassName()).isEqualTo( + ConfirmDeviceCredentialActivity.class.getName()); + } + // Test the case when FingerprintAuthenticateSidecar receives an error callback from the // framework or from another authentication client. The cancellation signal should not be set // to null because there may exist a running authentication client. diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java index e7e0b921ad7..204bfe5921d 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java @@ -50,9 +50,13 @@ import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; import android.content.Context; import android.content.Intent; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.Flags; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings.Global; import android.widget.TextView; @@ -86,9 +90,13 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivity; import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowBiometricManager; @RunWith(RobolectricTestRunner.class) @Config( @@ -98,18 +106,23 @@ import org.robolectric.shadows.ShadowApplication; ShadowStorageManager.class, ShadowUserManager.class, ShadowUtils.class, - ShadowInteractionJankMonitor.class + ShadowInteractionJankMonitor.class, + ShadowBiometricManager.class }) @Ignore("b/179136903: Tests failed with collapsing toolbar, plan to figure out root cause later.") public class ChooseLockGenericTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private ActivityController mActivityController; private FakeFeatureFactory mFakeFeatureFactory; private ChooseLockGenericFragment mFragment; private ChooseLockGeneric mActivity; + private ShadowBiometricManager mShadowBiometricManager; + private ShadowActivity mShadowActivity; @Mock private FingerprintManager mFingerprintManager; @Mock @@ -125,6 +138,7 @@ public class ChooseLockGenericTest { .postCreate(null) .resume() .get(); + mShadowActivity = Shadows.shadowOf(mActivity); Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1); mFragment = new ChooseLockGenericFragment(); @@ -294,6 +308,21 @@ public class ChooseLockGenericTest { assertThat(mActivity.isFinishing()).isFalse(); } + @Test + @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS) + public void onActivityResult_requestcode100_shouldLaunchBiometricPrompt() { + initActivity(null); + mShadowBiometricManager.setCanAuthenticate(true); + + mFragment.onActivityResult( + ChooseLockGenericFragment.CONFIRM_EXISTING_REQUEST, + Activity.RESULT_OK, null /* data */); + + assertThat(mActivity.isFinishing()).isFalse(); + assertThat(mShadowActivity.getNextStartedActivity().getComponent().getClassName()) + .isEqualTo(ConfirmDeviceCredentialActivity.class.getName()); + } + @Test public void onActivityResult_requestcode102_shouldFinish() { initActivity(null); @@ -336,6 +365,17 @@ public class ChooseLockGenericTest { assertThat(mActivity.isFinishing()).isTrue(); } + @Test + public void onActivityResult_requestcode105_shouldNotFinish() { + initActivity(null); + + mFragment.onActivityResult( + ChooseLockGenericFragment.BIOMETRIC_AUTH_REQUEST, Activity.RESULT_OK, + null /* data */); + + assertThat(mActivity.isFinishing()).isFalse(); + } + @Test public void securedScreenLock_notChangingConfig_notWaitForConfirmation_onStopFinishSelf() { Intent intent = new Intent().putExtra( @@ -676,6 +716,13 @@ public class ChooseLockGenericTest { mActivity = Robolectric.buildActivity(ChooseLockGeneric.InternalActivity.class, intent) .create().start().postCreate(null).resume().get(); mActivity.getSupportFragmentManager().beginTransaction().add(mFragment, null).commitNow(); + mShadowBiometricManager = Shadow.extract(mFragment.getContext().getSystemService( + BiometricManager.class)); + //TODO(b/352603684): Should be Authenticators.MANDATORY_BIOMETRICS, + // but it is not supported by ShadowBiometricManager + mShadowBiometricManager.setAuthenticatorType( + BiometricManager.Authenticators.BIOMETRIC_STRONG); + mShadowBiometricManager.setCanAuthenticate(false); } private static String capitalize(final String input) {