• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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