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