| /* |
| * Copyright (C) 2018 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.security; |
| |
| import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE; |
| |
| import static com.android.settings.security.SecuritySettings.UNIFY_LOCK_CONFIRM_PROFILE_REQUEST; |
| import static com.android.settings.security.SecuritySettings.UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST; |
| |
| import android.app.Activity; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| |
| 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.PreferenceControllerMixin; |
| 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.settingslib.RestrictedLockUtilsInternal; |
| import com.android.settingslib.RestrictedSwitchPreference; |
| import com.android.settingslib.core.AbstractPreferenceController; |
| import com.android.settingslib.transition.SettingsTransitionHelper; |
| |
| /** |
| * Controller for password unification/un-unification flows. |
| * |
| * When password is being unified, there may be two cases: |
| * 1. If device password will satisfy device-wide policies post-unification (when password policy |
| * set on the work challenge will be enforced on device password), the device password is |
| * preserved while work challenge is unified. Only the current work challenge is required |
| * in this flow. |
| * 2. Otherwise the user will need to enroll a new compliant device password before unification |
| * takes place. In this case we first confirm the current work challenge, then guide the user |
| * through an enrollment flow for the new device password, and finally unify the work challenge |
| * at the very end. |
| */ |
| public class LockUnificationPreferenceController extends AbstractPreferenceController |
| implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { |
| |
| private static final String KEY_UNIFICATION = "unification"; |
| |
| private static final int MY_USER_ID = UserHandle.myUserId(); |
| |
| private final UserManager mUm; |
| private final DevicePolicyManager mDpm; |
| private final LockPatternUtils mLockPatternUtils; |
| private final int mProfileUserId; |
| private final SettingsPreferenceFragment mHost; |
| |
| private RestrictedSwitchPreference mUnifyProfile; |
| |
| private final String mPreferenceKey; |
| |
| private LockscreenCredential mCurrentDevicePassword; |
| private LockscreenCredential mCurrentProfilePassword; |
| private boolean mRequireNewDevicePassword; |
| |
| @Override |
| public void displayPreference(PreferenceScreen screen) { |
| super.displayPreference(screen); |
| mUnifyProfile = screen.findPreference(mPreferenceKey); |
| } |
| |
| public LockUnificationPreferenceController(Context context, SettingsPreferenceFragment host) { |
| this(context, host, KEY_UNIFICATION); |
| } |
| |
| public LockUnificationPreferenceController( |
| Context context, SettingsPreferenceFragment host, String key) { |
| super(context); |
| mHost = host; |
| mUm = context.getSystemService(UserManager.class); |
| mDpm = context.getSystemService(DevicePolicyManager.class); |
| mLockPatternUtils = FeatureFactory.getFactory(context) |
| .getSecurityFeatureProvider() |
| .getLockPatternUtils(context); |
| mProfileUserId = Utils.getManagedProfileId(mUm, MY_USER_ID); |
| mCurrentDevicePassword = LockscreenCredential.createNone(); |
| mCurrentProfilePassword = LockscreenCredential.createNone(); |
| this.mPreferenceKey = key; |
| } |
| |
| @Override |
| public boolean isAvailable() { |
| return mProfileUserId != UserHandle.USER_NULL |
| && mUm.isManagedProfile(mProfileUserId); |
| } |
| |
| @Override |
| public String getPreferenceKey() { |
| return mPreferenceKey; |
| } |
| |
| @Override |
| public boolean onPreferenceChange(Preference preference, Object value) { |
| if (Utils.startQuietModeDialogIfNecessary(mContext, mUm, mProfileUserId)) { |
| return false; |
| } |
| final boolean useOneLock = (Boolean) value; |
| if (useOneLock) { |
| mRequireNewDevicePassword = !mDpm.isPasswordSufficientAfterProfileUnification( |
| UserHandle.myUserId(), mProfileUserId); |
| startUnification(); |
| } else { |
| final String title = mContext.getString(R.string.unlock_set_unlock_launch_picker_title); |
| final ChooseLockSettingsHelper.Builder builder = |
| new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost); |
| final boolean launched = builder.setRequestCode(UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST) |
| .setTitle(title) |
| .setReturnCredentials(true) |
| .setUserId(MY_USER_ID) |
| .show(); |
| |
| if (!launched) { |
| ununifyLocks(); |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public void updateState(Preference preference) { |
| if (mUnifyProfile != null) { |
| final boolean separate = |
| mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileUserId); |
| mUnifyProfile.setChecked(!separate); |
| if (separate) { |
| mUnifyProfile.setDisabledByAdmin(RestrictedLockUtilsInternal |
| .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_UNIFIED_PASSWORD, |
| mProfileUserId)); |
| } |
| } |
| } |
| |
| public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { |
| if (requestCode == UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST |
| && resultCode == Activity.RESULT_OK) { |
| mCurrentDevicePassword = |
| data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); |
| ununifyLocks(); |
| return true; |
| } else if (requestCode == UNIFY_LOCK_CONFIRM_PROFILE_REQUEST |
| && resultCode == Activity.RESULT_OK) { |
| mCurrentProfilePassword = |
| data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); |
| unifyLocks(); |
| return true; |
| } |
| return false; |
| } |
| |
| private void ununifyLocks() { |
| 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 String title = mDpm.getResources().getString( |
| WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE, |
| () -> mContext.getString(R.string.unlock_set_unlock_launch_picker_title_profile)); |
| final ChooseLockSettingsHelper.Builder builder = |
| new ChooseLockSettingsHelper.Builder(mHost.getActivity(), mHost); |
| final boolean launched = builder.setRequestCode(UNIFY_LOCK_CONFIRM_PROFILE_REQUEST) |
| .setTitle(title) |
| .setReturnCredentials(true) |
| .setUserId(mProfileUserId) |
| .show(); |
| if (!launched) { |
| // If profile has no lock, go straight to unification. |
| unifyLocks(); |
| // TODO: update relevant prefs. |
| // createPreferenceHierarchy(); |
| } |
| } |
| |
| private void unifyLocks() { |
| if (mRequireNewDevicePassword) { |
| promptForNewDeviceLockAndThenUnify(); |
| } else { |
| 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 promptForNewDeviceLockAndThenUnify() { |
| final Bundle extras = new Bundle(); |
| extras.putInt(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID, mProfileUserId); |
| extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL, |
| mCurrentProfilePassword); |
| new SubSettingLauncher(mContext) |
| .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName()) |
| .setTitleRes(R.string.lock_settings_picker_title) |
| .setSourceMetricsCategory(mHost.getMetricsCategory()) |
| .setArguments(extras) |
| .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE) |
| .launch(); |
| } |
| |
| } |