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