diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java index 60cc16e87e7..1f30e566022 100644 --- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java +++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java @@ -49,14 +49,17 @@ public class ActiveUnlockRequireBiometricSetup extends BiometricEnrollBase { @VisibleForTesting static final int BIOMETRIC_ENROLL_REQUEST = 1001; + private static final int ACTIVE_UNLOCK_REQUEST = 1002; private long mGkPwHandle; private boolean mNextClicked; + private ActiveUnlockStatusUtils mActiveUnlockStatusUtils; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activeunlock_require_biometric_setup); + mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(this); mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); Log.i(TAG, "mUserId = " + mUserId); mGkPwHandle = getIntent().getLongExtra(EXTRA_KEY_GK_PW_HANDLE, 0L); @@ -132,8 +135,10 @@ public class ActiveUnlockRequireBiometricSetup extends BiometricEnrollBase { CombinedBiometricStatusUtils combinedBiometricStatusUtils = new CombinedBiometricStatusUtils(this, mUserId); if (combinedBiometricStatusUtils.hasEnrolled()) { - // TODO(b/264813444): launch active unlock setting page in GmsCore without double - // authentication. + Intent activeUnlockIntent = mActiveUnlockStatusUtils.getIntent(); + if (activeUnlockIntent != null) { + startActivityForResult(activeUnlockIntent, ACTIVE_UNLOCK_REQUEST); + } } } mNextClicked = false; diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java index 05d4acb8f24..31c72aee722 100644 --- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java +++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java @@ -28,6 +28,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricStatusPreferenceController; import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener; +import com.android.settings.biometrics.combination.CombinedBiometricStatusUtils; import com.android.settingslib.RestrictedPreference; /** @@ -35,8 +36,7 @@ import com.android.settingslib.RestrictedPreference; * controls the ability to unlock the phone with watch authentication. */ public class ActiveUnlockStatusPreferenceController - extends BiometricStatusPreferenceController - implements LifecycleObserver, OnContentChangedListener { + extends BiometricStatusPreferenceController implements LifecycleObserver { /** * Preference key. * @@ -47,7 +47,9 @@ public class ActiveUnlockStatusPreferenceController @Nullable private PreferenceScreen mPreferenceScreen; @Nullable private String mSummary; private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils; + private final CombinedBiometricStatusUtils mCombinedBiometricStatusUtils; private final ActiveUnlockSummaryListener mActiveUnlockSummaryListener; + private final ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener; public ActiveUnlockStatusPreferenceController(@NonNull Context context) { this(context, KEY_ACTIVE_UNLOCK_SETTINGS); @@ -57,7 +59,31 @@ public class ActiveUnlockStatusPreferenceController @NonNull Context context, @NonNull String key) { super(context, key); mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(context); - mActiveUnlockSummaryListener = new ActiveUnlockSummaryListener(context, this); + mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(context, getUserId()); + OnContentChangedListener onSummaryChangedListener = new OnContentChangedListener() { + @Override + public void onContentChanged(String newContent) { + mSummary = newContent; + if (mPreference != null) { + mPreference.setSummary(getSummaryText()); + } + } + }; + OnContentChangedListener onDeviceNameChangedListener = + new OnContentChangedListener() { + + @Override + public void onContentChanged(String newContent) { + if (mPreference != null) { + mPreference.setSummary(getSummaryText()); + } + } + + }; + mActiveUnlockSummaryListener = + new ActiveUnlockSummaryListener(context, onSummaryChangedListener); + mActiveUnlockDeviceNameListener = + new ActiveUnlockDeviceNameListener(context, onDeviceNameChangedListener); } @@ -65,6 +91,7 @@ public class ActiveUnlockStatusPreferenceController @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { mActiveUnlockSummaryListener.subscribe(); + mActiveUnlockDeviceNameListener.subscribe(); } /** Resets the preference reference on resume. */ @@ -79,14 +106,7 @@ public class ActiveUnlockStatusPreferenceController @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onStop() { mActiveUnlockSummaryListener.unsubscribe(); - } - - @Override - public void onContentChanged(String newContent) { - mSummary = newContent; - if (mPreference != null) { - mPreference.setSummary(getSummaryText()); - } + mActiveUnlockDeviceNameListener.unsubscribe(); } @Override @@ -120,6 +140,15 @@ public class ActiveUnlockStatusPreferenceController @Override protected String getSummaryText() { + if (mActiveUnlockStatusUtils.useBiometricFailureLayout() + && !mActiveUnlockDeviceNameListener.hasEnrolled() + && !mCombinedBiometricStatusUtils.hasEnrolled()) { + @Nullable final String setupString = + mActiveUnlockStatusUtils.getSummaryWhenBiometricSetupRequired(); + if (setupString != null) { + return setupString; + } + } if (mSummary == null) { // return non-empty string to prevent re-sizing of the tile return " "; @@ -129,7 +158,6 @@ public class ActiveUnlockStatusPreferenceController @Override protected String getSettingsClassName() { - // TODO(b/264813445): direct user to face & fingerprint setup - return null; + return ActiveUnlockRequireBiometricSetup.class.getName(); } } diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java index 439f176d548..4ff2b87dd33 100644 --- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java +++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java @@ -242,6 +242,32 @@ public class ActiveUnlockStatusUtils { } } + /** + * Returns the summary of the active unlock preference when biometrics are needed to set up the + * feature. + */ + @Nullable + public String getSummaryWhenBiometricSetupRequired() { + final boolean faceAllowed = Utils.hasFaceHardware(mContext); + final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext); + + int summaryRes = getSetupBiometricRes(faceAllowed, fingerprintAllowed); + return summaryRes == 0 ? null : mContext.getString(summaryRes); + } + + @StringRes + private static int getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed) { + if (faceAllowed && fingerprintAllowed) { + return R.string.security_settings_activeunlock_require_face_fingerprint_setup_title; + } else if (faceAllowed) { + return R.string.security_settings_activeunlock_require_face_setup_title; + } else if (fingerprintAllowed) { + return R.string.security_settings_activeunlock_require_fingerprint_setup_title; + } else { + return 0; + } + } + private static String getFlagState() { return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME); } diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java index 0a1d29d4c1c..1f91a4667b4 100644 --- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java +++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java @@ -55,6 +55,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment { @VisibleForTesting 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 String SAVE_STATE_CONFIRM_CREDETIAL = "confirm_credential"; private static final String DO_NOT_FINISH_ACTIVITY = "do_not_finish_activity"; @@ -68,8 +69,9 @@ public abstract class BiometricsSettingsBase extends DashboardFragment { private boolean mConfirmCredential; @Nullable private FaceManager mFaceManager; @Nullable private FingerprintManager mFingerprintManager; - // Do not finish() if choosing/confirming credential, or showing fp/face settings - private boolean mDoNotFinishActivity; + // Do not finish() if choosing/confirming credential, showing fp/face settings, or launching + // active unlock + protected boolean mDoNotFinishActivity; @Nullable private String mRetryPreferenceKey = null; @Nullable private Bundle mRetryPreferenceExtra = null; @@ -132,7 +134,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment { } } - private boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) { + protected boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) { final String key = preference.getKey(); final Context context = requireActivity().getApplicationContext(); @@ -323,6 +325,14 @@ public abstract class BiometricsSettingsBase extends DashboardFragment { return resId == 0 ? "" : getString(resId); } + protected int getUserId() { + return mUserId; + } + + protected long getGkPwHandle() { + return mGkPwHandle; + } + @NonNull private String getUseClass2BiometricSummary() { boolean isFaceAllowed = false; diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java index a352e5a53e5..d0e986f175a 100644 --- a/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java +++ b/src/com/android/settings/biometrics/combination/CombinedBiometricSettings.java @@ -15,9 +15,14 @@ */ package com.android.settings.biometrics.combination; +import static com.android.settings.biometrics.activeunlock.ActiveUnlockStatusPreferenceController.KEY_ACTIVE_UNLOCK_SETTINGS; +import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE; + import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.Intent; import android.os.Bundle; +import android.os.UserHandle; import androidx.annotation.Nullable; import androidx.preference.Preference; @@ -25,6 +30,7 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.biometrics.activeunlock.ActiveUnlockContentListener.OnContentChangedListener; import com.android.settings.biometrics.activeunlock.ActiveUnlockDeviceNameListener; +import com.android.settings.biometrics.activeunlock.ActiveUnlockRequireBiometricSetup; import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.search.SearchIndexable; @@ -42,6 +48,7 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase { private static final String KEY_INTRO_PREFERENCE = "biometric_intro"; private ActiveUnlockStatusUtils mActiveUnlockStatusUtils; + private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils; @Nullable private ActiveUnlockDeviceNameListener mActiveUnlockDeviceNameListener; @Override @@ -55,6 +62,7 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(getActivity()); + mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(getActivity(), mUserId); if (mActiveUnlockStatusUtils.isAvailable()) { updateUiForActiveUnlock(); } @@ -121,6 +129,35 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase { return SettingsEnums.COMBINED_BIOMETRIC; } + @Override + protected boolean onRetryPreferenceTreeClick(Preference preference, final boolean retry) { + if (!mActiveUnlockStatusUtils.isAvailable() + || !KEY_ACTIVE_UNLOCK_SETTINGS.equals(preference.getKey())) { + return super.onRetryPreferenceTreeClick(preference, retry); + } + mDoNotFinishActivity = true; + Intent intent; + if (mActiveUnlockStatusUtils.useBiometricFailureLayout() + && mActiveUnlockDeviceNameListener != null + && !mActiveUnlockDeviceNameListener.hasEnrolled() + && !mCombinedBiometricStatusUtils.hasEnrolled()) { + intent = new Intent(getActivity(), ActiveUnlockRequireBiometricSetup.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + int userId = mUserId; + if (mUserId != UserHandle.USER_NULL) { + intent.putExtra(Intent.EXTRA_USER_ID, mUserId); + } + intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, getGkPwHandle()); + } else { + intent = mActiveUnlockStatusUtils.getIntent(); + } + if (intent != null) { + startActivityForResult(intent, ACTIVE_UNLOCK_REQUEST); + } + return true; + + } + @Override protected String getUseAnyBiometricSummary() { // either Active Unlock is not enabled or no device is enrolled. diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java index bf60d0173c0..5219a3a3265 100644 --- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceControllerTest.java @@ -32,6 +32,7 @@ import android.os.UserManager; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.testutils.ActiveUnlockTestUtils; import com.android.settings.testutils.shadow.ShadowDeviceConfig; import com.android.settingslib.RestrictedPreference; @@ -162,9 +163,57 @@ public class ActiveUnlockStatusPreferenceControllerTest { assertThat(mPreference.getSummary().toString()).isEqualTo(summary); } + @Test + public void biometricsNotSetUp_deviceNameIsNotSet_setupBiometricStringShown() { + ActiveUnlockTestUtils.enable(mContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT); + updateSummary("newSummary"); + mController.displayPreference(mPreferenceScreen); + + mController.onStart(); + idleMainLooper(); + + assertThat(mPreference.getSummary()).isEqualTo(mContext.getString( + R.string.security_settings_activeunlock_require_face_fingerprint_setup_title)); + } + + @Test + public void biometricNotSetUp_deviceNameIsSet_summaryShown() { + ActiveUnlockTestUtils.enable(mContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT); + String summary = "newSummary"; + updateSummary(summary); + updateDeviceName("deviceName"); + mController.displayPreference(mPreferenceScreen); + + mController.onStart(); + idleMainLooper(); + + assertThat(mPreference.getSummary()).isEqualTo(summary); + } + + @Test + public void biometricSetUp_summaryShown() { + when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true); + ActiveUnlockTestUtils.enable(mContext, ActiveUnlockStatusUtils.BIOMETRIC_FAILURE_LAYOUT); + String summary = "newSummary"; + updateSummary(summary); + mController.displayPreference(mPreferenceScreen); + + mController.onStart(); + idleMainLooper(); + + assertThat(mPreference.getSummary()).isEqualTo(summary); + } + private void updateSummary(String summary) { FakeContentProvider.setTileSummary(summary); mContext.getContentResolver().notifyChange(FakeContentProvider.URI, null /* observer */); idleMainLooper(); } + + private void updateDeviceName(String deviceName) { + FakeContentProvider.setDeviceName(deviceName); + mContext.getContentResolver().notifyChange(FakeContentProvider.URI, null /* observer */); + idleMainLooper(); + } + }