/* * 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 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.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; /** * 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 SecuritySettings mHost; private RestrictedSwitchPreference mUnifyProfile; private LockscreenCredential mCurrentDevicePassword; private LockscreenCredential mCurrentProfilePassword; private boolean mRequireNewDevicePassword; @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mUnifyProfile = screen.findPreference(KEY_UNIFICATION); } public LockUnificationPreferenceController(Context context, SecuritySettings host) { 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(); } @Override public boolean isAvailable() { return mProfileUserId != UserHandle.USER_NULL && mLockPatternUtils.isSeparateProfileChallengeAllowed(mProfileUserId); } @Override public String getPreferenceKey() { return KEY_UNIFICATION; } @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 helper = new ChooseLockSettingsHelper(mHost.getActivity(), mHost); if (!helper.launchConfirmationActivity( UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST, title, true /* returnCredentials */, MY_USER_ID)) { 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()) .setTitleRes(R.string.lock_settings_picker_title_profile) .setSourceMetricsCategory(mHost.getMetricsCategory()) .setArguments(extras) .launch(); } void startUnification() { // Confirm profile lock final String title = mContext.getString( R.string.unlock_set_unlock_launch_picker_title_profile); final ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(mHost.getActivity(), mHost); if (!helper.launchConfirmationActivity( UNIFY_LOCK_CONFIRM_PROFILE_REQUEST, title, true, mProfileUserId)) { // 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) .launch(); } }