diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index d44d727390c..e067c730d60 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -54,8 +54,10 @@ import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; +import android.hardware.biometrics.SensorProperties; import android.hardware.face.Face; import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.net.ConnectivityManager; @@ -927,6 +929,23 @@ public final class Utils extends com.android.settingslib.Utils { return hasFingerprintHardware(context) && hasFaceHardware(context); } + /** + * Return true if face is supported as Class 2 biometrics and above on the device, false + * otherwise. + */ + public static boolean isFaceNotConvenienceBiometric(@NonNull Context context) { + FaceManager faceManager = getFaceManagerOrNull(context); + if (faceManager != null) { + final List faceProperties = + faceManager.getSensorPropertiesInternal(); + if (!faceProperties.isEmpty()) { + final FaceSensorPropertiesInternal props = faceProperties.get(0); + return props.sensorStrength != SensorProperties.STRENGTH_CONVENIENCE; + } + } + return false; + } + /** * Launches an intent which may optionally have a user id defined. * @param fragment Fragment to use to launch the activity. diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java index 583a093dbc2..6ccaacb4b4d 100644 --- a/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java +++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceFacePreferenceController.java @@ -64,8 +64,8 @@ public class PrivateSpaceFacePreferenceController extends BiometricFaceStatusPre @Override public int getAvailabilityStatus() { return android.os.Flags.allowPrivateProfile() - && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace() - && android.multiuser.Flags.enablePrivateSpaceFeatures() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace() + && android.multiuser.Flags.enablePrivateSpaceFeatures() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } @@ -87,7 +87,8 @@ public class PrivateSpaceFacePreferenceController extends BiometricFaceStatusPre public void displayPreference(@NonNull PreferenceScreen screen) { super.displayPreference(screen); Preference preference = screen.findPreference(getPreferenceKey()); - if (!Utils.isMultipleBiometricsSupported(mContext)) { + if (!Utils.isMultipleBiometricsSupported(mContext) + && Utils.isFaceNotConvenienceBiometric(mContext)) { preference.setTitle(R.string.private_space_face_title); } } diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java index f88c9facccc..dd6518af67c 100644 --- a/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java +++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceFingerprintPreferenceController.java @@ -66,8 +66,8 @@ public class PrivateSpaceFingerprintPreferenceController @Override public int getAvailabilityStatus() { return android.os.Flags.allowPrivateProfile() - && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace() - && android.multiuser.Flags.enablePrivateSpaceFeatures() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace() + && android.multiuser.Flags.enablePrivateSpaceFeatures() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } @@ -89,7 +89,8 @@ public class PrivateSpaceFingerprintPreferenceController public void displayPreference(@NonNull PreferenceScreen screen) { super.displayPreference(screen); Preference preference = screen.findPreference(getPreferenceKey()); - if (!Utils.isMultipleBiometricsSupported(mContext)) { + if (!Utils.isMultipleBiometricsSupported(mContext) + || !Utils.isFaceNotConvenienceBiometric(mContext)) { preference.setTitle(R.string.private_space_fingerprint_title); } } diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java index ce017e31152..bf5748e6e2b 100644 --- a/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java +++ b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java @@ -73,13 +73,14 @@ public class UseOneLockSettingsFragment extends DashboardFragment { final List controllers = new ArrayList<>(); controllers.add(new UseOneLockControllerSwitch(context, this)); controllers.add(new PrivateSpaceLockController(context, this)); - if (Utils.isMultipleBiometricsSupported(context)) { + boolean isFaceAuthAllowed = Utils.isFaceNotConvenienceBiometric(context); + if (Utils.isMultipleBiometricsSupported(context) && isFaceAuthAllowed) { controllers.add(new FaceFingerprintUnlockController(context, getSettingsLifecycle())); } else if (Utils.hasFingerprintHardware(context)) { controllers.add( new PrivateSpaceFingerprintPreferenceController( context, "private_space_biometrics", getSettingsLifecycle())); - } else if (Utils.hasFaceHardware(context)) { + } else if (Utils.hasFaceHardware(context) && isFaceAuthAllowed) { controllers.add( new PrivateSpaceFacePreferenceController( context, "private_space_biometrics", getSettingsLifecycle())); diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index 0c555da2075..0c57b014506 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -16,6 +16,10 @@ package com.android.settings; +import static android.hardware.biometrics.SensorProperties.STRENGTH_CONVENIENCE; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertNull; @@ -43,6 +47,9 @@ import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.VectorDrawable; +import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; @@ -433,6 +440,69 @@ public class UtilsTest { assertNull(confirmCredentialString); } + @Test + public void isFaceNotConvenienceBiometric_faceStrengthStrong_shouldReturnTrue() { + FaceManager mockFaceManager = mock(FaceManager.class); + when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mockFaceManager); + doReturn(true).when(mPackageManager).hasSystemFeature(anyString()); + List props = List.of(new FaceSensorPropertiesInternal( + 0 /* id */, + STRENGTH_STRONG, + 1 /* maxTemplatesAllowed */, + new ArrayList<>() /* componentInfo */, + FaceSensorProperties.TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); + doReturn(props).when(mockFaceManager).getSensorPropertiesInternal(); + + assertThat(Utils.isFaceNotConvenienceBiometric(mContext)).isTrue(); + } + + @Test + public void isFaceNotConvenienceBiometric_faceStrengthWeak_shouldReturnTrue() { + FaceManager mockFaceManager = mock(FaceManager.class); + when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mockFaceManager); + doReturn(true).when(mPackageManager).hasSystemFeature(anyString()); + List props = List.of(new FaceSensorPropertiesInternal( + 0 /* id */, + STRENGTH_WEAK, + 1 /* maxTemplatesAllowed */, + new ArrayList<>() /* componentInfo */, + FaceSensorProperties.TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); + doReturn(props).when(mockFaceManager).getSensorPropertiesInternal(); + + assertThat(Utils.isFaceNotConvenienceBiometric(mContext)).isTrue(); + } + + @Test + public void isFaceNotConvenienceBiometric_faceStrengthConvenience_shouldReturnFalse() { + FaceManager mockFaceManager = mock(FaceManager.class); + when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mockFaceManager); + doReturn(true).when(mPackageManager).hasSystemFeature(anyString()); + List props = List.of(new FaceSensorPropertiesInternal( + 0 /* id */, + STRENGTH_CONVENIENCE, + 1 /* maxTemplatesAllowed */, + new ArrayList<>() /* componentInfo */, + FaceSensorProperties.TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); + doReturn(props).when(mockFaceManager).getSensorPropertiesInternal(); + + assertThat(Utils.isFaceNotConvenienceBiometric(mContext)).isFalse(); + } + + @Test + public void isFaceNotConvenienceBiometric_faceManagerNull_shouldReturnFalse() { + when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(null); + assertThat(Utils.isFaceNotConvenienceBiometric(mContext)).isFalse(); + } + private void setUpForConfirmCredentialString(boolean isEffectiveUserManagedProfile) { when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager); when(mMockUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(USER_ID);