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_DEVICE_REQUEST; 20 import static com.android.settings.security.SecuritySettings.UNIFY_LOCK_CONFIRM_PROFILE_REQUEST; 21 import static com.android.settings.security.SecuritySettings.UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST; 22 23 import android.app.Activity; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 31 import androidx.preference.Preference; 32 import androidx.preference.PreferenceScreen; 33 34 import com.android.internal.widget.LockPatternUtils; 35 import com.android.settings.R; 36 import com.android.settings.Utils; 37 import com.android.settings.core.PreferenceControllerMixin; 38 import com.android.settings.core.SubSettingLauncher; 39 import com.android.settings.overlay.FeatureFactory; 40 import com.android.settings.password.ChooseLockGeneric; 41 import com.android.settings.password.ChooseLockSettingsHelper; 42 import com.android.settingslib.RestrictedLockUtilsInternal; 43 import com.android.settingslib.RestrictedSwitchPreference; 44 import com.android.settingslib.core.AbstractPreferenceController; 45 46 /** 47 * Controller for password unification/un-unification flows. 48 * 49 * When password is being unified, there may be two cases: 50 * 1. If work password is not empty and satisfies device-wide policies (if any), it will be made 51 * into device-wide password. To do that we need both current device and profile passwords 52 * because both of them will be changed as a result. 53 * 2. Otherwise device-wide password is preserved. In this case we only need current profile 54 * password, but after unifying the passwords we proceed to ask the user for a new device 55 * password. 56 */ 57 public class LockUnificationPreferenceController extends AbstractPreferenceController 58 implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { 59 60 private static final String KEY_UNIFICATION = "unification"; 61 62 private static final int MY_USER_ID = UserHandle.myUserId(); 63 64 private final UserManager mUm; 65 private final DevicePolicyManager mDpm; 66 private final LockPatternUtils mLockPatternUtils; 67 private final int mProfileUserId; 68 private final SecuritySettings mHost; 69 70 private RestrictedSwitchPreference mUnifyProfile; 71 72 73 private byte[] mCurrentDevicePassword; 74 private byte[] mCurrentProfilePassword; 75 private boolean mKeepDeviceLock; 76 77 @Override displayPreference(PreferenceScreen screen)78 public void displayPreference(PreferenceScreen screen) { 79 super.displayPreference(screen); 80 mUnifyProfile = screen.findPreference(KEY_UNIFICATION); 81 } 82 LockUnificationPreferenceController(Context context, SecuritySettings host)83 public LockUnificationPreferenceController(Context context, SecuritySettings host) { 84 super(context); 85 mHost = host; 86 mUm = context.getSystemService(UserManager.class); 87 mDpm = context.getSystemService(DevicePolicyManager.class); 88 mLockPatternUtils = FeatureFactory.getFactory(context) 89 .getSecurityFeatureProvider() 90 .getLockPatternUtils(context); 91 mProfileUserId = Utils.getManagedProfileId(mUm, MY_USER_ID); 92 } 93 94 @Override isAvailable()95 public boolean isAvailable() { 96 return mProfileUserId != UserHandle.USER_NULL 97 && mLockPatternUtils.isSeparateProfileChallengeAllowed(mProfileUserId); 98 } 99 100 @Override getPreferenceKey()101 public String getPreferenceKey() { 102 return KEY_UNIFICATION; 103 } 104 105 @Override onPreferenceChange(Preference preference, Object value)106 public boolean onPreferenceChange(Preference preference, Object value) { 107 if (Utils.startQuietModeDialogIfNecessary(mContext, mUm, mProfileUserId)) { 108 return false; 109 } 110 final boolean useOneLock = (Boolean) value; 111 if (useOneLock) { 112 // Keep current device (personal) lock if the profile lock is empty or is not compliant 113 // with the policy on personal side. 114 mKeepDeviceLock = 115 mLockPatternUtils.getKeyguardStoredPasswordQuality(mProfileUserId) 116 < DevicePolicyManager.PASSWORD_QUALITY_SOMETHING 117 || !mDpm.isProfileActivePasswordSufficientForParent(mProfileUserId); 118 UnificationConfirmationDialog.newInstance(!mKeepDeviceLock).show(mHost); 119 } else { 120 final String title = mContext.getString(R.string.unlock_set_unlock_launch_picker_title); 121 final ChooseLockSettingsHelper helper = 122 new ChooseLockSettingsHelper(mHost.getActivity(), mHost); 123 if (!helper.launchConfirmationActivity( 124 UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST, 125 title, true /* returnCredentials */, MY_USER_ID)) { 126 ununifyLocks(); 127 } 128 } 129 return true; 130 } 131 132 @Override 133 public void updateState(Preference preference) { 134 if (mUnifyProfile != null) { 135 final boolean separate = 136 mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId); 137 mUnifyProfile.setChecked(!separate); 138 if (separate) { 139 mUnifyProfile.setDisabledByAdmin(RestrictedLockUtilsInternal 140 .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_UNIFIED_PASSWORD, 141 mProfileUserId)); 142 } 143 } 144 } 145 146 public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { 147 if (requestCode == UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST 148 && resultCode == Activity.RESULT_OK) { 149 ununifyLocks(); 150 return true; 151 } else if (requestCode == UNIFY_LOCK_CONFIRM_DEVICE_REQUEST 152 && resultCode == Activity.RESULT_OK) { 153 mCurrentDevicePassword = 154 data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 155 launchConfirmProfileLock(); 156 return true; 157 } else if (requestCode == UNIFY_LOCK_CONFIRM_PROFILE_REQUEST 158 && resultCode == Activity.RESULT_OK) { 159 mCurrentProfilePassword = 160 data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 161 unifyLocks(); 162 return true; 163 } 164 return false; 165 } 166 167 private void ununifyLocks() { 168 final Bundle extras = new Bundle(); 169 extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId); 170 new SubSettingLauncher(mContext) 171 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName()) 172 .setTitleRes(R.string.lock_settings_picker_title_profile) 173 .setSourceMetricsCategory(mHost.getMetricsCategory()) 174 .setArguments(extras) 175 .launch(); 176 } 177 178 /** Asks the user to confirm device lock (if there is one) and proceeds to ask profile lock. */ 179 private void launchConfirmDeviceAndProfileLock() { 180 final String title = mContext.getString( 181 R.string.unlock_set_unlock_launch_picker_title); 182 final ChooseLockSettingsHelper helper = 183 new ChooseLockSettingsHelper(mHost.getActivity(), mHost); 184 if (!helper.launchConfirmationActivity( 185 UNIFY_LOCK_CONFIRM_DEVICE_REQUEST, title, true, MY_USER_ID)) { 186 launchConfirmProfileLock(); 187 } 188 } 189 190 private void launchConfirmProfileLock() { 191 final String title = mContext.getString( 192 R.string.unlock_set_unlock_launch_picker_title_profile); 193 final ChooseLockSettingsHelper helper = 194 new ChooseLockSettingsHelper(mHost.getActivity(), mHost); 195 if (!helper.launchConfirmationActivity( 196 UNIFY_LOCK_CONFIRM_PROFILE_REQUEST, title, true, mProfileUserId)) { 197 unifyLocks(); 198 // TODO: update relevant prefs. 199 // createPreferenceHierarchy(); 200 } 201 } 202 203 void startUnification() { 204 // If the device lock stays the same, only confirm profile lock. Otherwise confirm both. 205 if (mKeepDeviceLock) { 206 launchConfirmProfileLock(); 207 } else { 208 launchConfirmDeviceAndProfileLock(); 209 } 210 } 211 212 private void unifyLocks() { 213 if (mKeepDeviceLock) { 214 unifyKeepingDeviceLock(); 215 promptForNewDeviceLock(); 216 } else { 217 unifyKeepingWorkLock(); 218 } 219 mCurrentDevicePassword = null; 220 mCurrentProfilePassword = null; 221 } 222 223 private void unifyKeepingWorkLock() { 224 final int profileQuality = 225 mLockPatternUtils.getKeyguardStoredPasswordQuality(mProfileUserId); 226 // PASSWORD_QUALITY_SOMETHING means pattern, everything above means PIN/password. 227 if (profileQuality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) { 228 mLockPatternUtils.saveLockPattern( 229 LockPatternUtils.byteArrayToPattern(mCurrentProfilePassword), 230 mCurrentDevicePassword, MY_USER_ID); 231 } else { 232 mLockPatternUtils.saveLockPassword( 233 mCurrentProfilePassword, mCurrentDevicePassword, profileQuality, MY_USER_ID); 234 } 235 mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false, 236 mCurrentProfilePassword); 237 final boolean profilePatternVisibility = 238 mLockPatternUtils.isVisiblePatternEnabled(mProfileUserId); 239 mLockPatternUtils.setVisiblePatternEnabled(profilePatternVisibility, MY_USER_ID); 240 } 241 242 private void unifyKeepingDeviceLock() { 243 mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false, 244 mCurrentProfilePassword); 245 } 246 247 private void promptForNewDeviceLock() { 248 new SubSettingLauncher(mContext) 249 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName()) 250 .setTitleRes(R.string.lock_settings_picker_title) 251 .setSourceMetricsCategory(mHost.getMetricsCategory()) 252 .launch(); 253 } 254 255 } 256