Add One Lock settings inside private space settings page

This includes below changes:
1. Add new sub settings page in Private Space settings with option to
   swithch between private profile lock and device screen lock
2. Call Private profile lock setup when swithcing to private lock from
   screen lock.
3. Preference to change private lock when a separate Private lock is set

Bug: 308862923
Test: atest UseOneLockPreferenceControllerTest,
PrivateSpaceLockControllerTest

Change-Id: I0a6d8c7dfbd0ffea19db03ffd6dfe7aa520c9684
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 31fb9e2..7406295 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1208,8 +1208,22 @@
     <string name="private_space_title">Private Space</string>
     <!-- Summary for the Private Space page. [CHAR LIMIT=NONE] -->
     <string name="private_space_summary">Hide apps in a private folder</string>
+    <!-- Description for the Private Space page. [CHAR LIMIT=NONE] -->
+    <string name="private_space_description">Hide apps in a private folder that only you can access</string>
     <!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
-    <string name="private_space_one_lock_title">Unlock using screen lock</string>
+    <string name="private_space_lock_title">Private Space lock</string>
+    <!-- Description for the Private Space one lock preference page. [CHAR LIMIT=NONE] -->
+    <string name="private_space_one_lock_summary">You can unlock Private Space the same way you unlock your device, or choose a different lock</string>
+    <!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
+    <string name="private_space_screen_lock_title">Use device screen lock</string>
+    <!-- Title for the Face and Fingerprint preference. [CHAR LIMIT=60] -->
+    <string name="private_space_biometric_title">Face &amp; Fingerprint Unlock</string>
+    <!-- Summary for the Face and Fingerprint preference when no biometric is set. [CHAR LIMIT=60] -->
+    <string name="private_space_biometric_summary">Tap to set up</string>
+    <!-- Summary for one lock when device screen lock is used as private profile lock. [CHAR LIMIT=60] -->
+    <string name="private_space_screen_lock_summary">Same as device screen lock</string>
+    <!-- Dialog message to choose a new lock for Private Space. [CHAR LIMIT=50] -->
+    <string name="private_space_new_lock_title">Choose a new lock for Private Space?</string>
     <!-- Title for the preference to hide Private Space. [CHAR LIMIT=60] -->
     <string name="private_space_hide_title">Hide when locked</string>
     <!-- Title for the hide Private Space setting. [CHAR LIMIT=60] -->
diff --git a/res/xml/private_space_settings.xml b/res/xml/private_space_settings.xml
index eb9fdae..48835fc 100644
--- a/res/xml/private_space_settings.xml
+++ b/res/xml/private_space_settings.xml
@@ -22,13 +22,25 @@
     android:title="@string/private_space_title"
     settings:searchable="false">
 
+    <com.android.settingslib.widget.IllustrationPreference
+        android:key="privatespace_hide_video"
+        settings:searchable="false"
+        settings:lottie_rawRes="@drawable/privatespace_placeholder_image"/>
+
+    <Preference
+        android:key="private_space_description"
+        android:summary="@string/private_space_description"
+        android:selectable="false"
+        settings:searchable="false" />
+
     <PreferenceCategory
         android:title="@string/security_header">
 
-        <SwitchPreferenceCompat
+        <Preference
             android:key="private_space_use_one_lock"
-            android:title="@string/private_space_one_lock_title"
-            settings:controller="com.android.settings.privatespace.UseOneLockController"
+            android:title="@string/private_space_lock_title"
+            android:fragment="com.android.settings.privatespace.onelock.UseOneLockSettingsFragment"
+            settings:controller="com.android.settings.privatespace.onelock.UseOneLockController"
             settings:searchable="false" />
 
         <Preference
@@ -51,4 +63,4 @@
 
     </PreferenceCategory>
 
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/res/xml/privatespace_one_lock.xml b/res/xml/privatespace_one_lock.xml
new file mode 100644
index 0000000..e078c17
--- /dev/null
+++ b/res/xml/privatespace_one_lock.xml
@@ -0,0 +1,44 @@
+<!--
+  ~ 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.
+  -->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/private_space_lock_title"
+    settings:searchable="false" >
+
+    <com.android.settingslib.widget.TopIntroPreference
+        android:title="@string/private_space_one_lock_summary"
+        settings:searchable="false" />
+
+    <com.android.settingslib.widget.MainSwitchPreference
+        android:key="private_lock_unification"
+        android:title="@string/private_space_screen_lock_title"
+        settings:searchable="false" />
+
+    <Preference
+        android:key="change_private_space_lock"
+        android:title="@string/private_space_lock_title"
+        android:summary="@string/unlock_set_unlock_mode_pattern"
+        settings:searchable="false" />
+
+    <Preference
+        android:key="private_space_biometrics"
+        android:title="@string/private_space_biometric_title"
+        android:summary="@string/private_space_biometric_summary"
+        android:fragment="com.android.settings.privatespace.onelock.FaceFingerprintUnlockFragment"
+        settings:searchable="false" />
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/privatespace/UseOneLockController.java b/src/com/android/settings/privatespace/UseOneLockController.java
deleted file mode 100644
index a94db57..0000000
--- a/src/com/android/settings/privatespace/UseOneLockController.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.privatespace;
-
-import android.content.Context;
-
-import com.android.settings.core.TogglePreferenceController;
-
-/** Represents the preference controller for using the same lock as the screen lock */
-public class UseOneLockController extends TogglePreferenceController {
-    public UseOneLockController(Context context, String preferenceKey) {
-        super(context, preferenceKey);
-    }
-
-    @Override
-    public int getAvailabilityStatus() {
-        return AVAILABLE;
-    }
-
-    @Override
-    public boolean isChecked() {
-        // TODO(b/293569406) Need to save this to a persistent store, maybe like SettingsProvider
-        return false;
-    }
-
-    @Override
-    public boolean setChecked(boolean isChecked) {
-        // TODO(b/293569406) Need to save this to a persistent store, maybe like SettingsProvider
-        return true;
-    }
-
-    @Override
-    public int getSliceHighlightMenuRes() {
-        return 0;
-    }
-}
diff --git a/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java b/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java
new file mode 100644
index 0000000..e976261
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/FaceFingerprintUnlockController.java
@@ -0,0 +1,57 @@
+/*
+ * 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.privatespace.onelock;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/** Represents the preference controller to enroll biometrics for private space lock. */
+public class FaceFingerprintUnlockController extends AbstractPreferenceController {
+    private static final String KEY_SET_UNSET_FACE_FINGERPRINT = "private_space_biometrics";
+
+    public FaceFingerprintUnlockController(Context context, SettingsPreferenceFragment host) {
+        super(context);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return false;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_SET_UNSET_FACE_FINGERPRINT;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        return TextUtils.equals(preference.getKey(), getPreferenceKey());
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        //TODO(b/308862923) : Add condition to check and enable when separate private lock is set.
+        preference.setSummary(mContext.getString(R.string.lock_settings_profile_unified_summary));
+        preference.setEnabled(false);
+    }
+}
diff --git a/src/com/android/settings/privatespace/onelock/PrivateSpaceLockController.java b/src/com/android/settings/privatespace/onelock/PrivateSpaceLockController.java
new file mode 100644
index 0000000..2783c1c
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/PrivateSpaceLockController.java
@@ -0,0 +1,134 @@
+/*
+ * 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.privatespace.onelock;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.transition.SettingsTransitionHelper;
+
+
+/** Represents the preference controller for changing private space lock. */
+public class PrivateSpaceLockController extends AbstractPreferenceController {
+    private static final String TAG = "PrivateSpaceLockContr";
+    private static final String KEY_CHANGE_PROFILE_LOCK =
+            "change_private_space_lock";
+
+    private final SettingsPreferenceFragment mHost;
+    private final UserManager mUserManager;
+    private final LockPatternUtils mLockPatternUtils;
+    private final int mProfileUserId;
+
+    public PrivateSpaceLockController(Context context, SettingsPreferenceFragment host) {
+        super(context);
+        mUserManager = context.getSystemService(UserManager.class);
+        mLockPatternUtils = FeatureFactory.getFeatureFactory()
+                .getSecurityFeatureProvider()
+                .getLockPatternUtils(context);
+        mHost = host;
+        UserHandle privateProfileHandle = PrivateSpaceMaintainer.getInstance(context)
+                .getPrivateProfileHandle();
+        if (privateProfileHandle != null) {
+            mProfileUserId = privateProfileHandle.getIdentifier();
+        } else {
+            mProfileUserId = -1;
+            Log.e(TAG, "Private profile user handle is not expected to be null.");
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_CHANGE_PROFILE_LOCK;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+            return false;
+        }
+        //Checks if the profile is in quiet mode and show a dialog to unpause the profile.
+        if (Utils.startQuietModeDialogIfNecessary(mContext, mUserManager,
+                mProfileUserId)) {
+            return false;
+        }
+        final Bundle extras = new Bundle();
+        extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
+        extras.putBoolean(HIDE_INSECURE_OPTIONS, true);
+        new SubSettingLauncher(mContext)
+                .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
+                .setSourceMetricsCategory(mHost.getMetricsCategory())
+                .setArguments(extras)
+                .setExtras(extras)
+                .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
+                .launch();
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId)) {
+            preference.setSummary(
+                    mContext.getString(getCredentialTypeResId(mProfileUserId)));
+            preference.setEnabled(true);
+        } else {
+            preference.setSummary(mContext.getString(
+                    R.string.lock_settings_profile_unified_summary));
+            preference.setEnabled(false);
+        }
+    }
+
+    private int getCredentialTypeResId(int userId) {
+        int credentialType = mLockPatternUtils.getCredentialTypeForUser(userId);
+        switch (credentialType) {
+            case CREDENTIAL_TYPE_PATTERN :
+                return R.string.unlock_set_unlock_mode_pattern;
+            case CREDENTIAL_TYPE_PIN:
+                return R.string.unlock_set_unlock_mode_pin;
+            case CREDENTIAL_TYPE_PASSWORD:
+                return R.string.unlock_set_unlock_mode_password;
+            default:
+                // This is returned for CREDENTIAL_TYPE_NONE
+                return R.string.unlock_set_unlock_mode_off;
+        }
+    }
+}
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockController.java b/src/com/android/settings/privatespace/onelock/UseOneLockController.java
new file mode 100644
index 0000000..5c461e0
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockController.java
@@ -0,0 +1,84 @@
+/*
+ * 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.privatespace.onelock;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
+
+/** Represents the preference controller for using the same lock as the screen lock */
+public class UseOneLockController extends BasePreferenceController {
+    private static final String TAG = "UseOneLockController";
+    private final LockPatternUtils mLockPatternUtils;
+    private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;
+
+    public UseOneLockController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext);
+        mLockPatternUtils = FeatureFactory.getFeatureFactory()
+                .getSecurityFeatureProvider()
+                .getLockPatternUtils(context);
+    }
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return 0;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        UserHandle privateProfileHandle = mPrivateSpaceMaintainer.getPrivateProfileHandle();
+        if (privateProfileHandle != null) {
+            int privateUserId = privateProfileHandle.getIdentifier();
+            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(privateUserId)) {
+                return mContext.getString(getCredentialTypeResId(privateUserId));
+            }
+        } else {
+            Log.w(TAG, "Did not find Private Space.");
+        }
+        return mContext.getString(R.string.private_space_screen_lock_summary);
+    }
+
+    private int getCredentialTypeResId(int userId) {
+        int credentialType = mLockPatternUtils.getCredentialTypeForUser(userId);
+        switch (credentialType) {
+            case CREDENTIAL_TYPE_PATTERN:
+                return R.string.unlock_set_unlock_mode_pattern;
+            case CREDENTIAL_TYPE_PIN:
+                return R.string.unlock_set_unlock_mode_pin;
+            case CREDENTIAL_TYPE_PASSWORD:
+                return R.string.unlock_set_unlock_mode_password;
+            default:
+                // This is returned for CREDENTIAL_TYPE_NONE
+                return R.string.unlock_set_unlock_mode_off;
+        }
+    }
+}
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
new file mode 100644
index 0000000..218b870
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockControllerSwitch.java
@@ -0,0 +1,216 @@
+/*
+ * 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.privatespace.onelock;
+
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
+import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
+import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST;
+import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settings.privatespace.PrivateProfileContextHelperActivity;
+import com.android.settings.privatespace.PrivateSpaceMaintainer;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.transition.SettingsTransitionHelper;
+import com.android.settingslib.widget.MainSwitchPreference;
+
+/** Represents the preference controller for using the same lock as the screen lock */
+public class UseOneLockControllerSwitch extends AbstractPreferenceController
+        implements Preference.OnPreferenceChangeListener {
+    private static final String TAG = "UseOneLockSwitch";
+    private static final String KEY_UNIFICATION = "private_lock_unification";
+    private final String mPreferenceKey;
+    private final SettingsPreferenceFragment mHost;
+    private final LockPatternUtils mLockPatternUtils;
+    private final UserManager mUserManager;
+    private final int mProfileUserId;
+    private final UserHandle mUserHandle;
+    private LockscreenCredential mCurrentDevicePassword;
+    private LockscreenCredential mCurrentProfilePassword;
+    private MainSwitchPreference mUnifyProfile;
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mUnifyProfile = screen.findPreference(mPreferenceKey);
+    }
+    public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host) {
+        this(context, host, KEY_UNIFICATION);
+    }
+
+    public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host,
+              String key) {
+        super(context);
+        mHost = host;
+        mUserManager = context.getSystemService(UserManager.class);
+        mLockPatternUtils = FeatureFactory.getFeatureFactory().getSecurityFeatureProvider()
+                  .getLockPatternUtils(context);
+        mUserHandle =  PrivateSpaceMaintainer.getInstance(context).getPrivateProfileHandle();
+        mProfileUserId = mUserHandle != null ? mUserHandle.getIdentifier() : -1;
+        mCurrentDevicePassword = LockscreenCredential.createNone();
+        mCurrentProfilePassword = LockscreenCredential.createNone();
+        this.mPreferenceKey = key;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return mPreferenceKey;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object value) {
+        //Checks if the profile is in quiet mode and show a dialog to unpause the profile.
+        if (Utils.startQuietModeDialogIfNecessary(mContext, mUserManager, mProfileUserId)) {
+            return false;
+        }
+        final boolean useOneLock = (Boolean) value;
+        if (useOneLock) {
+            startUnification();
+        } else {
+            showAlertDialog();
+        }
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        if (mUnifyProfile != null) {
+            final boolean separate =
+                      mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId);
+            mUnifyProfile.setChecked(!separate);
+        }
+    }
+
+    /** Method to handle onActivityResult */
+    public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST
+                  && resultCode == Activity.RESULT_OK) {
+            mCurrentDevicePassword =
+                      data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            separateLocks();
+            return true;
+        } else if (requestCode == UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST
+                  && resultCode == Activity.RESULT_OK) {
+            mCurrentProfilePassword =
+                      data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
+            unifyLocks();
+            return true;
+        }
+        return false;
+    }
+
+    private void separateLocks() {
+        final Bundle extras = new Bundle();
+        extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
+        extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mCurrentDevicePassword);
+        new SubSettingLauncher(mContext)
+                  .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
+                  .setSourceMetricsCategory(mHost.getMetricsCategory())
+                  .setArguments(extras)
+                  .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
+                  .launch();
+    }
+
+    /** Unify primary and profile locks. */
+    public void startUnification() {
+        // Confirm profile lock
+        final ChooseLockSettingsHelper.Builder builder =
+                  new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost);
+        final boolean launched = builder.setRequestCode(UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST)
+                  .setReturnCredentials(true)
+                  .setUserId(mProfileUserId)
+                  .show();
+        if (!launched) {
+            // If profile has no lock, go straight to unification.
+            unifyLocks();
+        }
+    }
+
+    private void unifyLocks() {
+        unifyKeepingDeviceLock();
+        if (mCurrentDevicePassword != null) {
+            mCurrentDevicePassword.zeroize();
+            mCurrentDevicePassword = null;
+        }
+        if (mCurrentProfilePassword != null) {
+            mCurrentProfilePassword.zeroize();
+            mCurrentProfilePassword = null;
+        }
+    }
+
+    private void unifyKeepingDeviceLock() {
+        mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false,
+                  mCurrentProfilePassword);
+    }
+
+    private void showAlertDialog() {
+        if (mUserHandle == null) {
+            Log.e(TAG, "Private profile user handle is not expected to be null");
+            mUnifyProfile.setChecked(true);
+            return;
+        }
+        new AlertDialog.Builder(mContext)
+                  .setMessage(R.string.private_space_new_lock_title)
+                  .setPositiveButton(
+                            R.string.privatespace_set_lock_label,
+                            (dialog, which) -> {
+                                Intent intent = new Intent(mContext,
+                                          PrivateProfileContextHelperActivity.class);
+                                intent.putExtra(EXTRA_ACTION_TYPE, SET_LOCK_ACTION);
+                                ((Activity) mContext).startActivityForResultAsUser(intent,
+                                          UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST,
+                                          /*Options*/ null, mUserHandle);
+                            })
+                  .setNegativeButton(R.string.privatespace_cancel_label,
+                            (DialogInterface dialog, int which) -> {
+                                mUnifyProfile.setChecked(true);
+                                dialog.dismiss();
+                            })
+                  .setOnCancelListener(
+                            (DialogInterface dialog) -> {
+                                mUnifyProfile.setChecked(true);
+                                dialog.dismiss();
+                            })
+                  .show();
+    }
+}
diff --git a/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
new file mode 100644
index 0000000..36f8448
--- /dev/null
+++ b/src/com/android/settings/privatespace/onelock/UseOneLockSettingsFragment.java
@@ -0,0 +1,69 @@
+/*
+ * 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.privatespace.onelock;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UseOneLockSettingsFragment extends DashboardFragment {
+    private static final String TAG = "UseOneLockSettings";
+    public static final int UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST = 1;
+    public static final int UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST = 2;
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.PRIVATE_SPACE_SETTINGS;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.privatespace_one_lock;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new UseOneLockControllerSwitch(context, this));
+        controllers.add(new PrivateSpaceLockController(context, this));
+        controllers.add(new FaceFingerprintUnlockController(context, this));
+        return controllers;
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        if (use(UseOneLockControllerSwitch.class)
+                  .handleActivityResult(requestCode, resultCode, data)) {
+            return;
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceLockControllerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceLockControllerTest.java
new file mode 100644
index 0000000..0d9db7e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceLockControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.privatespace;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.privatespace.onelock.PrivateSpaceLockController;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PrivateSpaceLockControllerTest {
+    @Mock
+    private Context mContext;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Mock SettingsPreferenceFragment mSettingsPreferenceFragment;
+    @Mock
+    LockPatternUtils mLockPatternUtils;
+
+    private Preference mPreference;
+    private PrivateSpaceLockController mPrivateSpaceLockController;
+
+    /** Required setup before a test. */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = ApplicationProvider.getApplicationContext();
+        final String preferenceKey = "unlock_set_or_change_private_lock";
+
+        mPreference = new Preference(ApplicationProvider.getApplicationContext());
+        mPreference.setKey(preferenceKey);
+
+        final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+        when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
+                .thenReturn(mLockPatternUtils);
+
+        mPrivateSpaceLockController = new PrivateSpaceLockController(mContext,
+                mSettingsPreferenceFragment);
+    }
+
+    /** Tests that the controller is always available. */
+    @Test
+    public void getAvailabilityStatus_returnsAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        assertThat(mPrivateSpaceLockController.isAvailable()).isEqualTo(true);
+    }
+
+    /** Tests that preference is disabled and summary says same as device lock. */
+    @Test
+    public void getSummary_whenScreenLock() {
+        doReturn(false).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mPrivateSpaceLockController.updateState(mPreference);
+        assertThat(mPreference.isEnabled()).isFalse();
+        assertThat(mPreference.getSummary().toString()).isEqualTo("Same as device screen lock");
+    }
+
+    /** Tests that preference is enabled and summary is Pattern. */
+    @Test
+    public void getSummary_whenProfileLockPattern() {
+        doReturn(true)
+                .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        doReturn(CREDENTIAL_TYPE_PATTERN)
+                .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mPrivateSpaceLockController.updateState(mPreference);
+        assertThat(mPreference.isEnabled()).isTrue();
+        assertThat(mPreference.getSummary().toString()).isEqualTo("Pattern");
+    }
+
+    /** Tests that preference is enabled and summary is Pin. */
+    @Test
+    public void getSummary_whenProfileLockPin() {
+        doReturn(true).when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        doReturn(CREDENTIAL_TYPE_PIN).when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mPrivateSpaceLockController.updateState(mPreference);
+        assertThat(mPreference.isEnabled()).isTrue();
+        assertThat(mPreference.getSummary().toString()).isEqualTo("PIN");
+    }
+
+    /** Tests that preference is enabled and summary is Password. */
+    @Test
+    public void getSummary_whenProfileLockPassword() {
+        doReturn(true)
+                .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        doReturn(CREDENTIAL_TYPE_PASSWORD)
+                .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mPrivateSpaceLockController.updateState(mPreference);
+        assertThat(mPreference.isEnabled()).isTrue();
+        assertThat(mPreference.getSummary().toString()).isEqualTo("Password");
+    }
+}
diff --git a/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java b/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java
index e7ebb37..744a8ec 100644
--- a/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/UseOneLockControllerTest.java
@@ -16,36 +16,105 @@
 
 package com.android.settings.privatespace;
 
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
 import static com.google.common.truth.Truth.assertThat;
 
-import android.content.Context;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
 
+import android.content.Context;
+import android.os.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.preference.Preference;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.privatespace.onelock.UseOneLockController;
+import com.android.settings.testutils.FakeFeatureFactory;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 public class UseOneLockControllerTest {
     @Mock private Context mContext;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private UseOneLockController mUseOneLockController;
+    private Preference mPreference;
+
+    @Mock
+    LockPatternUtils mLockPatternUtils;
 
     /** Required setup before a test. */
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mContext = ApplicationProvider.getApplicationContext();
         final String preferenceKey = "private_space_use_one_lock";
+        mPreference = new Preference(mContext);
 
+        final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+        when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
+                .thenReturn(mLockPatternUtils);
         mUseOneLockController = new UseOneLockController(mContext, preferenceKey);
+
     }
 
     /** Tests that the controller is always available. */
     @Test
     public void getAvailabilityStatus_returnsAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
         assertThat(mUseOneLockController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
     }
+
+
+    /** Tests that summary in controller is Pattern. */
+    @Test
+    public void getSummary_whenProfileLockPattern() {
+        doReturn(true)
+                .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        doReturn(CREDENTIAL_TYPE_PATTERN)
+                .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mUseOneLockController.updateState(mPreference);
+        assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("Pattern");
+    }
+
+    /** Tests that summary in controller is PIN. */
+    @Test
+    public void getSummary_whenProfileLockPin() {
+        doReturn(true)
+                .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        doReturn(CREDENTIAL_TYPE_PIN).when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mUseOneLockController.updateState(mPreference);
+        assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("PIN");
+    }
+
+    /** Tests that summary in controller is Password. */
+    @Test
+    public void getSummary_whenProfileLockPassword() {
+        doReturn(true)
+                .when(mLockPatternUtils).isSeparateProfileChallengeEnabled(anyInt());
+        doReturn(CREDENTIAL_TYPE_PASSWORD)
+                .when(mLockPatternUtils).getCredentialTypeForUser(anyInt());
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mUseOneLockController.updateState(mPreference);
+        assertThat(mUseOneLockController.getSummary().toString()).isEqualTo("Password");
+    }
 }