Merge "Route to active unlock component"
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java
index 60cc16e..1f30e56 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockRequireBiometricSetup.java
@@ -49,14 +49,17 @@
 
     @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 @@
             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 05d4acb..31c72ae 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusPreferenceController.java
@@ -28,6 +28,7 @@
 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 @@
  * 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 @@
     @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 @@
             @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 @@
     @OnLifecycleEvent(Lifecycle.Event.ON_START)
     public void onStart() {
         mActiveUnlockSummaryListener.subscribe();
+        mActiveUnlockDeviceNameListener.subscribe();
     }
 
     /** Resets the preference reference on resume. */
@@ -79,14 +106,7 @@
     @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 @@
 
     @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 @@
 
     @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 439f176..4ff2b87 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
@@ -242,6 +242,32 @@
         }
     }
 
+    /**
+     * 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 0a1d29d..1f91a46 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -55,6 +55,7 @@
     @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 @@
     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 @@
         }
     }
 
-    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 @@
         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 a352e5a..d0e986f 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 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 @@
     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 void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mActiveUnlockStatusUtils = new ActiveUnlockStatusUtils(getActivity());
+        mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(getActivity(), mUserId);
         if (mActiveUnlockStatusUtils.isAvailable()) {
             updateUiForActiveUnlock();
         }
@@ -122,6 +130,35 @@
     }
 
     @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.
         if (mActiveUnlockDeviceNameListener == null
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 bf60d01..5219a3a 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 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 @@
         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();
+    }
+
 }