1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.privatespace.onelock; 18 19 import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS; 20 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE; 21 import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE; 22 import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION; 23 import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST; 24 import static com.android.settings.privatespace.onelock.UseOneLockSettingsFragment.UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST; 25 26 import android.app.Activity; 27 import android.app.AlertDialog; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.Intent; 31 import android.os.Bundle; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.util.Log; 35 36 import androidx.annotation.Nullable; 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceScreen; 39 40 import com.android.internal.widget.LockPatternUtils; 41 import com.android.internal.widget.LockscreenCredential; 42 import com.android.settings.R; 43 import com.android.settings.SettingsPreferenceFragment; 44 import com.android.settings.Utils; 45 import com.android.settings.core.SubSettingLauncher; 46 import com.android.settings.overlay.FeatureFactory; 47 import com.android.settings.password.ChooseLockGeneric; 48 import com.android.settings.password.ChooseLockSettingsHelper; 49 import com.android.settings.privatespace.PrivateProfileContextHelperActivity; 50 import com.android.settings.privatespace.PrivateSpaceMaintainer; 51 import com.android.settingslib.core.AbstractPreferenceController; 52 import com.android.settingslib.transition.SettingsTransitionHelper; 53 import com.android.settingslib.widget.MainSwitchPreference; 54 55 /** Represents the preference controller for using the same lock as the screen lock */ 56 public class UseOneLockControllerSwitch extends AbstractPreferenceController 57 implements Preference.OnPreferenceChangeListener { 58 private static final String TAG = "UseOneLockSwitch"; 59 private static final String KEY_UNIFICATION = "private_lock_unification"; 60 private final String mPreferenceKey; 61 private final SettingsPreferenceFragment mHost; 62 private final LockPatternUtils mLockPatternUtils; 63 private final UserManager mUserManager; 64 private final int mProfileUserId; 65 private final UserHandle mUserHandle; 66 private LockscreenCredential mCurrentDevicePassword; 67 private LockscreenCredential mCurrentProfilePassword; 68 private MainSwitchPreference mUnifyProfile; 69 70 @Override displayPreference(PreferenceScreen screen)71 public void displayPreference(PreferenceScreen screen) { 72 super.displayPreference(screen); 73 mUnifyProfile = screen.findPreference(mPreferenceKey); 74 } UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host)75 public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host) { 76 this(context, host, KEY_UNIFICATION); 77 } 78 UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host, String key)79 public UseOneLockControllerSwitch(Context context, SettingsPreferenceFragment host, 80 String key) { 81 super(context); 82 mHost = host; 83 mUserManager = context.getSystemService(UserManager.class); 84 mLockPatternUtils = FeatureFactory.getFeatureFactory().getSecurityFeatureProvider() 85 .getLockPatternUtils(context); 86 mUserHandle = PrivateSpaceMaintainer.getInstance(context).getPrivateProfileHandle(); 87 mProfileUserId = mUserHandle != null ? mUserHandle.getIdentifier() : -1; 88 mCurrentDevicePassword = LockscreenCredential.createNone(); 89 mCurrentProfilePassword = LockscreenCredential.createNone(); 90 this.mPreferenceKey = key; 91 } 92 93 @Override getPreferenceKey()94 public String getPreferenceKey() { 95 return mPreferenceKey; 96 } 97 98 @Override isAvailable()99 public boolean isAvailable() { 100 return android.os.Flags.allowPrivateProfile() 101 && android.multiuser.Flags.enablePrivateSpaceFeatures(); 102 } 103 104 @Override onPreferenceChange(Preference preference, Object value)105 public boolean onPreferenceChange(Preference preference, Object value) { 106 //Checks if the profile is in quiet mode and show a dialog to unpause the profile. 107 if (Utils.startQuietModeDialogIfNecessary(mContext, mUserManager, mProfileUserId)) { 108 return false; 109 } 110 final boolean useOneLock = (Boolean) value; 111 if (useOneLock) { 112 startUnification(); 113 } else { 114 showAlertDialog(); 115 } 116 return true; 117 } 118 119 @Override updateState(Preference preference)120 public void updateState(Preference preference) { 121 if (mUnifyProfile != null) { 122 final boolean separate = 123 mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId); 124 mUnifyProfile.setChecked(!separate); 125 } 126 } 127 128 /** Method to handle onActivityResult */ handleActivityResult(int requestCode, int resultCode, @Nullable Intent data)129 public boolean handleActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 130 if (requestCode == UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST 131 && resultCode == Activity.RESULT_OK && data != null) { 132 mCurrentDevicePassword = 133 data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 134 separateLocks(); 135 return true; 136 } else if (requestCode == UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST 137 && resultCode == Activity.RESULT_OK && data != null) { 138 mCurrentProfilePassword = 139 data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 140 unifyLocks(); 141 return true; 142 } 143 return false; 144 } 145 separateLocks()146 private void separateLocks() { 147 final Bundle extras = new Bundle(); 148 extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId); 149 extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mCurrentDevicePassword); 150 new SubSettingLauncher(mContext) 151 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName()) 152 .setSourceMetricsCategory(mHost.getMetricsCategory()) 153 .setArguments(extras) 154 .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE) 155 .launch(); 156 } 157 158 /** Unify primary and profile locks. */ startUnification()159 public void startUnification() { 160 // Confirm profile lock 161 final ChooseLockSettingsHelper.Builder builder = 162 new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost); 163 final boolean launched = builder.setRequestCode(UNIFY_PRIVATE_LOCK_WITH_DEVICE_REQUEST) 164 .setReturnCredentials(true) 165 .setUserId(mProfileUserId) 166 .show(); 167 if (!launched) { 168 // If profile has no lock, go straight to unification. 169 unifyLocks(); 170 } 171 } 172 unifyLocks()173 private void unifyLocks() { 174 unifyKeepingDeviceLock(); 175 if (mCurrentDevicePassword != null) { 176 mCurrentDevicePassword.zeroize(); 177 mCurrentDevicePassword = null; 178 } 179 if (mCurrentProfilePassword != null) { 180 mCurrentProfilePassword.zeroize(); 181 mCurrentProfilePassword = null; 182 } 183 } 184 unifyKeepingDeviceLock()185 private void unifyKeepingDeviceLock() { 186 mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false, 187 mCurrentProfilePassword); 188 } 189 showAlertDialog()190 private void showAlertDialog() { 191 if (mUserHandle == null) { 192 Log.e(TAG, "Private profile user handle is not expected to be null"); 193 mUnifyProfile.setChecked(true); 194 return; 195 } 196 new AlertDialog.Builder(mContext) 197 .setTitle(R.string.private_space_new_lock_title) 198 .setMessage(R.string.private_space_new_lock_message) 199 .setPositiveButton( 200 R.string.private_space_set_lock_label, 201 (dialog, which) -> { 202 startSeparateLockSetup(); 203 }) 204 .setNegativeButton(R.string.private_space_cancel_label, 205 (DialogInterface dialog, int which) -> { 206 mUnifyProfile.setChecked(true); 207 dialog.dismiss(); 208 }) 209 .setOnCancelListener( 210 (DialogInterface dialog) -> { 211 mUnifyProfile.setChecked(true); 212 dialog.dismiss(); 213 }) 214 .show(); 215 } 216 startSeparateLockSetup()217 private void startSeparateLockSetup() { 218 if (android.multiuser.Flags.modifyPrivateSpaceSecondaryUnlockSetupFlow()) { 219 final Bundle extras = new Bundle(); 220 extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId); 221 extras.putBoolean(HIDE_INSECURE_OPTIONS, true); 222 extras.putInt(EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE, 223 R.string.private_space_lock_setup_title); 224 new SubSettingLauncher(mContext).setDestination(ChooseLockGeneric 225 .ChooseLockGenericFragment.class.getName()) 226 .setSourceMetricsCategory(mHost.getMetricsCategory()) 227 .setArguments(extras) 228 .setExtras(extras) 229 .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE) 230 .launch(); 231 } else { 232 Intent intent = new Intent(mContext, PrivateProfileContextHelperActivity.class); 233 intent.putExtra(EXTRA_ACTION_TYPE, SET_LOCK_ACTION); 234 ((Activity) mContext).startActivityForResultAsUser(intent, 235 UNUNIFY_PRIVATE_LOCK_FROM_DEVICE_REQUEST, /*Options*/ null, mUserHandle); 236 } 237 } 238 } 239