Post-enroll settings screen shows class 3 strings
Test: atest FaceSettingsFooterPreferenceControllerTest
Test: manual test
Bug: 274959218
Change-Id: I2e30666baf36a48a47e9ed9e4e046cefc805995a
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 5b6474e..3569485 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -155,6 +155,30 @@
<string name="security_settings_face_enroll_introduction_control_consent_message" product="tablet"></string>
<!-- Message on the face enrollment introduction page that explains privacy controls for face unlock when asking for parental consent. [CHAR LIMIT=NONE] -->
<string name="security_settings_face_enroll_introduction_control_consent_message" product="device"></string>
+ <!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer" product="default">Use your face to unlock your phone or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face.\n\nYour phone can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer" product="tablet">Use your face to unlock your tablet or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the tablet can unlock it when you don\u2019t intend to.\n\nYour tablet can be unlocked by someone else if it\u2019s held up to your face.\n\nYour tablet can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer" product="device">Use your face to unlock your device or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the device can unlock it when you don\u2019t intend to.\n\nYour device can be unlocked by someone else if it\u2019s held up to your face.\n\nYour device can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when attention checking is not supported. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_attention_not_supported" product="default">Use your face to unlock your phone or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour phone can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when attention checking is not supported. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_attention_not_supported" product="tablet">Use your face to unlock your tablet or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the tablet can unlock it when you don\u2019t intend to.\n\nYour tablet can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour tablet can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when attention checking is not supported. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_attention_not_supported" product="device">Use your face to unlock your device or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the device can unlock it when you don\u2019t intend to.\n\nYour device can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour device can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when face is class 3. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_class3" product="default">Use your face to unlock your phone or verify it\u2019s you, like when you sign in to apps or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face.\n\nYour phone can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when face is class 3. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_class3" product="tablet">Use your face to unlock your tablet or verify it\u2019s you, like when you sign in to apps or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the tablet can unlock it when you don\u2019t intend to.\n\nYour tablet can be unlocked by someone else if it\u2019s held up to your face.\n\nYour tablet can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when face is class 3. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_class3" product="device">Use your face to unlock your device or verify it\u2019s you, like when you sign in to apps or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the device can unlock it when you don\u2019t intend to.\n\nYour device can be unlocked by someone else if it\u2019s held up to your face.\n\nYour device can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when face is class 3 and attention checking is not supported. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_class3_attention_not_supported" product="default">Use your face to unlock your phone or verify it\u2019s you, like when you sign in to apps or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour phone can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when face is class 3 and attention checking is not supported. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_class3_attention_not_supported" product="tablet">Use your face to unlock your tablet or verify it\u2019s you, like when you sign in to apps or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the tablet can unlock it when you don\u2019t intend to.\n\nYour tablet can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour tablet can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
+ <!-- Text shown in face settings explaining what your face can be used for. Used when face is class 3 and attention checking is not supported. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_footer_class3_attention_not_supported" product="device">Use your face to unlock your device or verify it\u2019s you, like when you sign in to apps or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the device can unlock it when you don\u2019t intend to.\n\nYour device can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour device can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
<!-- Introduction detail message shown in fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
<string name="security_settings_fingerprint_enroll_introduction_v3_message">Use your fingerprint to unlock your <xliff:g id="deviceName" example="Pixel">%s</xliff:g> or verify it\u2019s you, like when you sign in to apps or approve a purchase</string>
<!-- Introduction detail message shown in fingerprint enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 44f6503..8e6cf23 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -729,11 +729,7 @@
<string name="security_settings_face_settings_remove_face_model">Delete face model</string>
<!-- Button text in face settings which lets the user enroll their face [CHAR LIMIT=40] -->
<string name="security_settings_face_settings_enroll">Set up Face Unlock</string>
- <!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
- <string name="security_settings_face_settings_footer">Use your face to unlock your phone or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face.\n\nYour phone can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
- <!-- Text shown in face settings explaining what your face can be used for. Used when attention checking is not supported. [CHAR LIMIT=NONE] -->
- <string name="security_settings_face_settings_footer_attention_not_supported">Use your face to unlock your phone or for authentication in apps, like when you sign in or approve a purchase.\n\nKeep in mind:\nYou can only have one face set up at a time. To add another face, delete the current one.\n\nLooking at the phone can unlock it when you don\u2019t intend to.\n\nYour phone can be unlocked by someone else if it\u2019s held up to your face, even if your eyes are closed.\n\nYour phone can be unlocked by someone who looks a lot like you, like an identical sibling.</string>
- <!-- Dialog title shown when the user removes an enrollment [CHAR LIMIT=35] -->
+ <!-- Dialog title shown when the user removes an enrollment [CHAR LIMIT=35] -->
<string name="security_settings_face_settings_remove_dialog_title">Delete face model?</string>
<!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_remove_dialog_details">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java
index ba1047b..261166b 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceController.java
@@ -18,8 +18,15 @@
import android.content.Context;
import android.content.Intent;
+import android.hardware.biometrics.SensorProperties;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -27,18 +34,47 @@
import com.android.settings.utils.AnnotationSpan;
import com.android.settingslib.HelpUtils;
+import java.util.List;
+
/**
* Footer for face settings showing the help text and help link.
*/
public class FaceSettingsFooterPreferenceController extends BasePreferenceController {
-
+ private static final String TAG = "FaceSettingsFooterPreferenceController";
private static final String ANNOTATION_URL = "url";
-
- private FaceFeatureProvider mProvider;
+ private final FaceFeatureProvider mProvider;
+ private Preference mPreference;
+ private boolean mIsFaceStrong;
public FaceSettingsFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mProvider = FeatureFactory.getFactory(context).getFaceFeatureProvider();
+ FaceManager faceManager = context.getSystemService(FaceManager.class);
+ faceManager.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ @NonNull List<FaceSensorPropertiesInternal> 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;
+ updateState(mPreference);
+ }
+ });
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(mPreferenceKey);
}
@Override
@@ -55,10 +91,17 @@
final AnnotationSpan.LinkInfo linkInfo =
new AnnotationSpan.LinkInfo(mContext, ANNOTATION_URL, helpIntent);
- final int footerRes = mProvider.isAttentionSupported(mContext)
- ? R.string.security_settings_face_settings_footer
- : R.string.security_settings_face_settings_footer_attention_not_supported;
-
+ int footerRes;
+ boolean isAttentionSupported = mProvider.isAttentionSupported(mContext);
+ if (mIsFaceStrong) {
+ footerRes = isAttentionSupported
+ ? R.string.security_settings_face_settings_footer_class3
+ : R.string.security_settings_face_settings_footer_attention_not_supported;
+ } else {
+ footerRes = isAttentionSupported
+ ? R.string.security_settings_face_settings_footer
+ : R.string.security_settings_face_settings_footer_class3_attention_not_supported;
+ }
preference.setTitle(AnnotationSpan.linkify(
mContext.getText(footerRes), linkInfo));
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java
new file mode 100644
index 0000000..bf3d9f7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/biometrics/face/FaceSettingsFooterPreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 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.settings.biometrics.face;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.widget.FooterPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class FaceSettingsFooterPreferenceControllerTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ private static final String PREF_KEY = "security_face_footer";
+ @Mock
+ private FaceManager mFaceManager;
+ @Captor
+ private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor;
+ private Preference mPreference;
+ private Context mContext;
+ private FaceSettingsFooterPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ if (Looper.myLooper() == null) {
+ Looper.prepare(); // needed to create the preference screen
+ }
+ ShadowApplication.getInstance().setSystemService(Context.FACE_SERVICE, mFaceManager);
+
+ mController = new FaceSettingsFooterPreferenceController(mContext, PREF_KEY);
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ mPreference = new FooterPreference(mContext);
+ mPreference.setKey(PREF_KEY);
+ screen.addPreference(mPreference);
+
+ mController.displayPreference(screen);
+ }
+
+ @Test
+ public void isSliceable_returnFalse() {
+ assertThat(mController.isSliceable()).isFalse();
+ }
+
+ @Test
+ public void testString_faceNotClass3() throws RemoteException {
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.security_settings_face_settings_footer));
+
+ List<FaceSensorPropertiesInternal> 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);
+
+ assertThat(mPreference.getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.security_settings_face_settings_footer));
+ }
+
+ @Test
+ public void testString_faceClass3() throws RemoteException {
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.security_settings_face_settings_footer));
+
+ List<FaceSensorPropertiesInternal> 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);
+
+ assertThat(mPreference.getTitle().toString()).isEqualTo(
+ mContext.getString(R.string.security_settings_face_settings_footer_class3));
+ }
+}