1 /* 2 * Copyright (C) 2018 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.security; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE; 20 21 import static com.android.settings.security.SecuritySettings.UNIFY_LOCK_CONFIRM_PROFILE_REQUEST; 22 import static com.android.settings.security.SecuritySettings.UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST; 23 24 import android.app.Activity; 25 import android.app.admin.DevicePolicyManager; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.os.Bundle; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 32 import androidx.preference.Preference; 33 import androidx.preference.PreferenceScreen; 34 35 import com.android.internal.widget.LockPatternUtils; 36 import com.android.internal.widget.LockscreenCredential; 37 import com.android.settings.R; 38 import com.android.settings.SettingsPreferenceFragment; 39 import com.android.settings.Utils; 40 import com.android.settings.core.PreferenceControllerMixin; 41 import com.android.settings.core.SubSettingLauncher; 42 import com.android.settings.overlay.FeatureFactory; 43 import com.android.settings.password.ChooseLockGeneric; 44 import com.android.settings.password.ChooseLockSettingsHelper; 45 import com.android.settingslib.RestrictedLockUtilsInternal; 46 import com.android.settingslib.RestrictedSwitchPreference; 47 import com.android.settingslib.core.AbstractPreferenceController; 48 import com.android.settingslib.transition.SettingsTransitionHelper; 49 50 /** 51 * Controller for password unification/un-unification flows. 52 * 53 * When password is being unified, there may be two cases: 54 * 1. If device password will satisfy device-wide policies post-unification (when password policy 55 * set on the work challenge will be enforced on device password), the device password is 56 * preserved while work challenge is unified. Only the current work challenge is required 57 * in this flow. 58 * 2. Otherwise the user will need to enroll a new compliant device password before unification 59 * takes place. In this case we first confirm the current work challenge, then guide the user 60 * through an enrollment flow for the new device password, and finally unify the work challenge 61 * at the very end. 62 */ 63 public class LockUnificationPreferenceController extends AbstractPreferenceController 64 implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 65 66 private static final String KEY_UNIFICATION = "unification"; 67 68 private static final int MY_USER_ID = UserHandle.myUserId(); 69 70 private final UserManager mUm; 71 private final DevicePolicyManager mDpm; 72 private final LockPatternUtils mLockPatternUtils; 73 private final int mProfileUserId; 74 private final SettingsPreferenceFragment mHost; 75 76 private RestrictedSwitchPreference mUnifyProfile; 77 78 private final String mPreferenceKey; 79 80 private LockscreenCredential mCurrentDevicePassword; 81 private LockscreenCredential mCurrentProfilePassword; 82 private boolean mRequireNewDevicePassword; 83 84 @Override displayPreference(PreferenceScreen screen)85 public void displayPreference(PreferenceScreen screen) { 86 super.displayPreference(screen); 87 mUnifyProfile = screen.findPreference(mPreferenceKey); 88 } 89 LockUnificationPreferenceController(Context context, SettingsPreferenceFragment host)90 public LockUnificationPreferenceController(Context context, SettingsPreferenceFragment host) { 91 this(context, host, KEY_UNIFICATION); 92 } 93 LockUnificationPreferenceController( Context context, SettingsPreferenceFragment host, String key)94 public LockUnificationPreferenceController( 95 Context context, SettingsPreferenceFragment host, String key) { 96 super(context); 97 mHost = host; 98 mUm = context.getSystemService(UserManager.class); 99 mDpm = context.getSystemService(DevicePolicyManager.class); 100 mLockPatternUtils = FeatureFactory.getFeatureFactory() 101 .getSecurityFeatureProvider() 102 .getLockPatternUtils(context); 103 mProfileUserId = Utils.getManagedProfileId(mUm, MY_USER_ID); 104 mCurrentDevicePassword = LockscreenCredential.createNone(); 105 mCurrentProfilePassword = LockscreenCredential.createNone(); 106 this.mPreferenceKey = key; 107 } 108 109 @Override isAvailable()110 public boolean isAvailable() { 111 return mProfileUserId != UserHandle.USER_NULL 112 && mUm.isManagedProfile(mProfileUserId); 113 } 114 115 @Override getPreferenceKey()116 public String getPreferenceKey() { 117 return mPreferenceKey; 118 } 119 120 @Override onPreferenceChange(Preference preference, Object value)121 public boolean onPreferenceChange(Preference preference, Object value) { 122 if (Utils.startQuietModeDialogIfNecessary(mContext, mUm, mProfileUserId)) { 123 return false; 124 } 125 final boolean useOneLock = (Boolean) value; 126 if (useOneLock) { 127 mRequireNewDevicePassword = !mDpm.isPasswordSufficientAfterProfileUnification( 128 UserHandle.myUserId(), mProfileUserId); 129 startUnification(); 130 } else { 131 final String title = mContext.getString(R.string.unlock_set_unlock_launch_picker_title); 132 final ChooseLockSettingsHelper.Builder builder = 133 new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost); 134 final boolean launched = builder.setRequestCode(UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST) 135 .setTitle(title) 136 .setReturnCredentials(true) 137 .setUserId(MY_USER_ID) 138 .show(); 139 140 if (!launched) { 141 ununifyLocks(); 142 } 143 } 144 return true; 145 } 146 147 @Override updateState(Preference preference)148 public void updateState(Preference preference) { 149 if (mUnifyProfile != null) { 150 final boolean separate = 151 mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId); 152 mUnifyProfile.setChecked(!separate); 153 if (separate) { 154 mUnifyProfile.setDisabledByAdmin(RestrictedLockUtilsInternal 155 .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_UNIFIED_PASSWORD, 156 mProfileUserId)); 157 } 158 } 159 } 160 handleActivityResult(int requestCode, int resultCode, Intent data)161 public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { 162 if (requestCode == UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST 163 && resultCode == Activity.RESULT_OK) { 164 mCurrentDevicePassword = 165 data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 166 ununifyLocks(); 167 return true; 168 } else if (requestCode == UNIFY_LOCK_CONFIRM_PROFILE_REQUEST 169 && resultCode == Activity.RESULT_OK) { 170 mCurrentProfilePassword = 171 data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 172 unifyLocks(); 173 return true; 174 } 175 return false; 176 } 177 ununifyLocks()178 private void ununifyLocks() { 179 final Bundle extras = new Bundle(); 180 extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId); 181 extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mCurrentDevicePassword); 182 new SubSettingLauncher(mContext) 183 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName()) 184 .setSourceMetricsCategory(mHost.getMetricsCategory()) 185 .setArguments(extras) 186 .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE) 187 .launch(); 188 } 189 190 /** 191 * Unify primary and profile locks. 192 */ startUnification()193 public void startUnification() { 194 // Confirm profile lock 195 final String title = mDpm.getResources().getString( 196 WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE, 197 () -> mContext.getString(R.string.unlock_set_unlock_launch_picker_title_profile)); 198 final ChooseLockSettingsHelper.Builder builder = 199 new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost); 200 final boolean launched = builder.setRequestCode(UNIFY_LOCK_CONFIRM_PROFILE_REQUEST) 201 .setTitle(title) 202 .setReturnCredentials(true) 203 .setUserId(mProfileUserId) 204 .show(); 205 if (!launched) { 206 // If profile has no lock, go straight to unification. 207 unifyLocks(); 208 // TODO: update relevant prefs. 209 // createPreferenceHierarchy(); 210 } 211 } 212 unifyLocks()213 private void unifyLocks() { 214 if (mRequireNewDevicePassword) { 215 promptForNewDeviceLockAndThenUnify(); 216 } else { 217 unifyKeepingDeviceLock(); 218 } 219 if (mCurrentDevicePassword != null) { 220 mCurrentDevicePassword.zeroize(); 221 mCurrentDevicePassword = null; 222 } 223 if (mCurrentProfilePassword != null) { 224 mCurrentProfilePassword.zeroize(); 225 mCurrentProfilePassword = null; 226 } 227 } 228 unifyKeepingDeviceLock()229 private void unifyKeepingDeviceLock() { 230 mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false, 231 mCurrentProfilePassword); 232 } 233 promptForNewDeviceLockAndThenUnify()234 private void promptForNewDeviceLockAndThenUnify() { 235 final Bundle extras = new Bundle(); 236 extras.putInt(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID, mProfileUserId); 237 extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL, 238 mCurrentProfilePassword); 239 new SubSettingLauncher(mContext) 240 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName()) 241 .setTitleRes(R.string.lock_settings_picker_title) 242 .setSourceMetricsCategory(mHost.getMetricsCategory()) 243 .setArguments(extras) 244 .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE) 245 .launch(); 246 } 247 248 } 249