diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml index 3569485bd74..2f01c4725bd 100644 --- a/res-product/values/strings.xml +++ b/res-product/values/strings.xml @@ -83,6 +83,12 @@ Use your face to unlock your tablet, authorize purchases, or sign in to apps. Use your face to unlock your device, authorize purchases, or sign in to apps. + + Use your face to unlock your phone or for authentication in apps, like when you sign in to apps or approve a purchase. + + Use your face to unlock your tablet or for authentication in apps, like when you sign in to apps or approve a purchase. + + Use your face to unlock your device or for authentication in apps, like when you sign in to apps or approve a purchase. Allow your child to use their face to unlock their phone diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java index a19138e2673..2a350f4abd5 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java @@ -489,15 +489,18 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase finish(); } - @Override - protected void initViews() { - super.initViews(); - + protected void updateDescriptionText() { if (mBiometricUnlockDisabledByAdmin && !mParentalConsentRequired) { setDescriptionText(getDescriptionDisabledByAdmin()); } } + @Override + protected void initViews() { + super.initViews(); + updateDescriptionText(); + } + @NonNull protected PorterDuffColorFilter getIconColorFilter() { if (mIconColorFilter == null) { diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java index f434a67f485..3e8c45852d7 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java @@ -26,7 +26,10 @@ import android.content.Intent; import android.content.res.Configuration; import android.hardware.SensorPrivacyManager; import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.SensorProperties; import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.os.Bundle; import android.os.UserHandle; import android.text.Html; @@ -60,6 +63,8 @@ import com.google.android.setupcompat.template.FooterButton; import com.google.android.setupcompat.util.WizardManagerHelper; import com.google.android.setupdesign.span.LinkSpan; +import java.util.List; + /** * Provides introductory info about face unlock and prompts the user to agree before starting face * enrollment. @@ -71,6 +76,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { @Nullable private FooterButton mPrimaryFooterButton; @Nullable private FooterButton mSecondaryFooterButton; @Nullable private SensorPrivacyManager mSensorPrivacyManager; + private boolean mIsFaceStrong; @Override protected void onCancelButtonClick(View view) { @@ -154,14 +160,6 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { inControlMessage.setMovementMethod(LinkMovementMethod.getInstance()); lessSecure.setText(getLessSecureMessage()); - // Set up and show the "less secure" info section if necessary. - if (getResources().getBoolean(R.bool.config_face_intro_show_less_secure)) { - final LinearLayout infoRowLessSecure = findViewById(R.id.info_row_less_secure); - final ImageView iconLessSecure = findViewById(R.id.icon_less_secure); - infoRowLessSecure.setVisibility(View.VISIBLE); - iconLessSecure.getBackground().setColorFilter(getIconColorFilter()); - } - // Set up and show the "require eyes" info section if necessary. if (getResources().getBoolean(R.bool.config_face_intro_show_require_eyes)) { final LinearLayout infoRowRequireEyes = findViewById(R.id.info_row_require_eyes); @@ -172,6 +170,28 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { infoMessageRequireEyes.setText(getInfoMessageRequireEyes()); } + mFaceManager.addAuthenticatorsRegisteredCallback( + new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + @NonNull List sensors) { + if (sensors.isEmpty()) { + Log.e(TAG, "No sensors"); + return; + } + + boolean isFaceStrong = sensors.get(0).sensorStrength + == SensorProperties.STRENGTH_STRONG; + if (mIsFaceStrong == isFaceStrong) { + return; + } + mIsFaceStrong = isFaceStrong; + onFaceStrengthChanged(); + } + }); + + onFaceStrengthChanged(); + // This path is an entry point for SetNewPasswordController, e.g. // adb shell am start -a android.app.action.SET_NEW_PASSWORD if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) { @@ -554,6 +574,15 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { return R.string.security_settings_face_enroll_introduction_more; } + @Override + protected void updateDescriptionText() { + if (mIsFaceStrong) { + setDescriptionText(getString( + R.string.security_settings_face_enroll_introduction_message_class3)); + } + super.updateDescriptionText(); + } + @NonNull protected static Intent setSkipPendingEnroll(@Nullable Intent data) { if (data == null) { @@ -562,4 +591,16 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true); return data; } + + private void onFaceStrengthChanged() { + // Set up and show the "less secure" info section if necessary. + if (!mIsFaceStrong && getResources().getBoolean( + R.bool.config_face_intro_show_less_secure)) { + final LinearLayout infoRowLessSecure = findViewById(R.id.info_row_less_secure); + final ImageView iconLessSecure = findViewById(R.id.icon_less_secure); + infoRowLessSecure.setVisibility(View.VISIBLE); + iconLessSecure.getBackground().setColorFilter(getIconColorFilter()); + } + updateDescriptionText(); + } } diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java index 591f88d79fa..378d4aee3a0 100644 --- a/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceEnrollIntroductionTest.java @@ -32,7 +32,9 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; @@ -41,6 +43,9 @@ import android.content.Intent; import android.content.res.Configuration; import android.hardware.face.Face; import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.os.UserHandle; import android.view.View; import android.widget.TextView; @@ -72,6 +77,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -99,6 +106,8 @@ public class FaceEnrollIntroductionTest { private FaceManager mFaceManager; @Mock private LockPatternUtils mLockPatternUtils; + @Captor + private ArgumentCaptor mCaptor; private Context mContext; private ActivityController mController; @@ -127,7 +136,6 @@ public class FaceEnrollIntroductionTest { return mConfirmingCredentials; } - public FaceManager mOverrideFaceManager = null; @NonNull public GateKeeperAction mGateKeeperAction = GateKeeperAction.CALL_SUPER; @@ -145,12 +153,6 @@ public class FaceEnrollIntroductionTest { } } - @Nullable - @Override - protected FaceManager getFaceManager() { - return mOverrideFaceManager; - } - @Override protected boolean launchPostureGuidance() { return super.launchPostureGuidance(); @@ -186,7 +188,7 @@ public class FaceEnrollIntroductionTest { mController = Robolectric.buildActivity( TestFaceEnrollIntroduction.class, testIntent); mActivity = (TestFaceEnrollIntroduction) spy(mController.get()); - mActivity.mOverrideFaceManager = mFaceManager; + doReturn(mFaceManager).when(mActivity).getFaceManager(); when(mActivity.getPostureGuidanceIntent()).thenReturn(null); when(mContext.getApplicationContext()).thenReturn(mContext); when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager); @@ -229,7 +231,6 @@ public class FaceEnrollIntroductionTest { }).when(mFaceManager).generateChallenge(anyInt(), any()); mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); mActivity = (TestFaceEnrollIntroduction) mController.get(); - mActivity.mOverrideFaceManager = mFaceManager; } private GlifLayout getGlifLayout(Activity activity) { @@ -310,12 +311,51 @@ public class FaceEnrollIntroductionTest { } @Test - public void testFaceEnrollIntroduction_hasDescription() { + public void testFaceEnrollIntroduction_hasDescription_weakFace() throws Exception { setupActivity(); + verify(mFaceManager).addAuthenticatorsRegisteredCallback(mCaptor.capture()); CharSequence desc = getGlifLayout(mActivity).getDescriptionText(); assertThat(desc.toString()).isEqualTo( mContext.getString(R.string.security_settings_face_enroll_introduction_message)); + + List props = List.of(new FaceSensorPropertiesInternal( + 0 /* id */, + FaceSensorProperties.STRENGTH_WEAK, + 1 /* maxTemplatesAllowed */, + new ArrayList<>() /* componentInfo */, + FaceSensorProperties.TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); + mCaptor.getValue().onAllAuthenticatorsRegistered(props); + desc = getGlifLayout(mActivity).getDescriptionText(); + + assertThat(desc.toString()).isEqualTo( + mContext.getString(R.string.security_settings_face_enroll_introduction_message)); + } + + @Test + public void testFaceEnrollIntroduction_hasDescriptionNoLessSecure_strongFace() + throws Exception { + setupActivity(); + verify(mFaceManager).addAuthenticatorsRegisteredCallback(mCaptor.capture()); + + List props = List.of(new FaceSensorPropertiesInternal( + 0 /* id */, + FaceSensorProperties.STRENGTH_STRONG, + 1 /* maxTemplatesAllowed */, + new ArrayList<>() /* componentInfo */, + FaceSensorProperties.TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); + mCaptor.getValue().onAllAuthenticatorsRegistered(props); + CharSequence desc = getGlifLayout(mActivity).getDescriptionText(); + + assertThat(desc.toString()).isEqualTo( + mContext.getString( + R.string.security_settings_face_enroll_introduction_message_class3)); } @Test @@ -412,7 +452,6 @@ public class FaceEnrollIntroductionTest { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); mActivity = (TestFaceEnrollIntroduction) mController.get(); - mActivity.mOverrideFaceManager = mFaceManager; mController.create(); @@ -432,7 +471,6 @@ public class FaceEnrollIntroductionTest { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); mActivity = (TestFaceEnrollIntroduction) mController.get(); - mActivity.mOverrideFaceManager = mFaceManager; mController.create(); @@ -450,7 +488,6 @@ public class FaceEnrollIntroductionTest { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); mActivity = (TestFaceEnrollIntroduction) mController.get(); - mActivity.mOverrideFaceManager = mFaceManager; mController.create(); @@ -470,7 +507,6 @@ public class FaceEnrollIntroductionTest { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); mActivity = (TestFaceEnrollIntroduction) mController.get(); - mActivity.mOverrideFaceManager = mFaceManager; mController.create(); mController.start(); @@ -489,7 +525,6 @@ public class FaceEnrollIntroductionTest { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class, intent); mActivity = (TestFaceEnrollIntroduction) mController.get(); - mActivity.mOverrideFaceManager = mFaceManager; mController.create(); mController.start();