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.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 device password will satisfy device-wide policies post-unification (when password policy
51  *      set on the work challenge will be enforced on device password), the device password is
52  *      preserved while work challenge is unified. Only the current work challenge is required
53  *      in this flow.
54  *   2. Otherwise the user will need to enroll a new compliant device password before unification
55  *      takes place. In this case we first confirm the current work challenge, then guide the user
56  *      through an enrollment flow for the new device password, and finally unify the work challenge
57  *      at the very end.
58  */
59 public class LockUnificationPreferenceController extends AbstractPreferenceController
60         implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
61 
62     private static final String KEY_UNIFICATION = "unification";
63 
64     private static final int MY_USER_ID = UserHandle.myUserId();
65 
66     private final UserManager mUm;
67     private final DevicePolicyManager mDpm;
68     private final LockPatternUtils mLockPatternUtils;
69     private final int mProfileUserId;
70     private final SecuritySettings mHost;
71 
72     private RestrictedSwitchPreference mUnifyProfile;
73 
74 
75     private LockscreenCredential mCurrentDevicePassword;
76     private LockscreenCredential mCurrentProfilePassword;
77     private boolean mRequireNewDevicePassword;
78 
79     @Override
displayPreference(PreferenceScreen screen)80     public void displayPreference(PreferenceScreen screen) {
81         super.displayPreference(screen);
82         mUnifyProfile = screen.findPreference(KEY_UNIFICATION);
83     }
84 
LockUnificationPreferenceController(Context context, SecuritySettings host)85     public LockUnificationPreferenceController(Context context, SecuritySettings host) {
86         super(context);
87         mHost = host;
88         mUm = context.getSystemService(UserManager.class);
89         mDpm = context.getSystemService(DevicePolicyManager.class);
90         mLockPatternUtils = FeatureFactory.getFactory(context)
91                 .getSecurityFeatureProvider()
92                 .getLockPatternUtils(context);
93         mProfileUserId = Utils.getManagedProfileId(mUm, MY_USER_ID);
94         mCurrentDevicePassword = LockscreenCredential.createNone();
95         mCurrentProfilePassword = LockscreenCredential.createNone();
96     }
97 
98     @Override
isAvailable()99     public boolean isAvailable() {
100         return mProfileUserId != UserHandle.USER_NULL
101                 && mLockPatternUtils.isSeparateProfileChallengeAllowed(mProfileUserId);
102     }
103 
104     @Override
getPreferenceKey()105     public String getPreferenceKey() {
106         return KEY_UNIFICATION;
107     }
108 
109     @Override
onPreferenceChange(Preference preference, Object value)110     public boolean onPreferenceChange(Preference preference, Object value) {
111         if (Utils.startQuietModeDialogIfNecessary(mContext, mUm, mProfileUserId)) {
112             return false;
113         }
114         final boolean useOneLock = (Boolean) value;
115         if (useOneLock) {
116             mRequireNewDevicePassword = !mDpm.isPasswordSufficientAfterProfileUnification(
117                     UserHandle.myUserId(), mProfileUserId);
118             startUnification();
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
updateState(Preference preference)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 
handleActivityResult(int requestCode, int resultCode, Intent data)146     public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
147         if (requestCode == UNUNIFY_LOCK_CONFIRM_DEVICE_REQUEST
148                 && resultCode == Activity.RESULT_OK) {
149             mCurrentDevicePassword =
150                     data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
151             ununifyLocks();
152             return true;
153         } else if (requestCode == UNIFY_LOCK_CONFIRM_PROFILE_REQUEST
154                 && resultCode == Activity.RESULT_OK) {
155             mCurrentProfilePassword =
156                     data.getParcelableExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
157             unifyLocks();
158             return true;
159         }
160         return false;
161     }
162 
ununifyLocks()163     private void ununifyLocks() {
164         final Bundle extras = new Bundle();
165         extras.putInt(Intent.EXTRA_USER_ID, mProfileUserId);
166         extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, mCurrentDevicePassword);
167         new SubSettingLauncher(mContext)
168                 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
169                 .setTitleRes(R.string.lock_settings_picker_title_profile)
170                 .setSourceMetricsCategory(mHost.getMetricsCategory())
171                 .setArguments(extras)
172                 .launch();
173     }
174 
startUnification()175     void startUnification() {
176         // Confirm profile lock
177         final String title = mContext.getString(
178                 R.string.unlock_set_unlock_launch_picker_title_profile);
179         final ChooseLockSettingsHelper helper =
180                 new ChooseLockSettingsHelper(mHost.getActivity(), mHost);
181         if (!helper.launchConfirmationActivity(
182                 UNIFY_LOCK_CONFIRM_PROFILE_REQUEST, title, true, mProfileUserId)) {
183             // If profile has no lock, go straight to unification.
184             unifyLocks();
185             // TODO: update relevant prefs.
186             // createPreferenceHierarchy();
187         }
188     }
189 
unifyLocks()190     private void unifyLocks() {
191         if (mRequireNewDevicePassword) {
192             promptForNewDeviceLockAndThenUnify();
193         } else {
194             unifyKeepingDeviceLock();
195         }
196         if (mCurrentDevicePassword != null) {
197             mCurrentDevicePassword.zeroize();
198             mCurrentDevicePassword = null;
199         }
200         if (mCurrentProfilePassword != null) {
201             mCurrentProfilePassword.zeroize();
202             mCurrentProfilePassword = null;
203         }
204     }
205 
unifyKeepingDeviceLock()206     private void unifyKeepingDeviceLock() {
207         mLockPatternUtils.setSeparateProfileChallengeEnabled(mProfileUserId, false,
208                 mCurrentProfilePassword);
209     }
210 
promptForNewDeviceLockAndThenUnify()211     private void promptForNewDeviceLockAndThenUnify() {
212         final Bundle extras = new Bundle();
213         extras.putInt(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID, mProfileUserId);
214         extras.putParcelable(ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL,
215                 mCurrentProfilePassword);
216         new SubSettingLauncher(mContext)
217                 .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
218                 .setTitleRes(R.string.lock_settings_picker_title)
219                 .setSourceMetricsCategory(mHost.getMetricsCategory())
220                 .setArguments(extras)
221                 .launch();
222     }
223 
224 }
225