1 /*
2  * Copyright (C) 2017 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.password;
18 
19 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
20 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
21 
22 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
23 
24 import android.app.admin.DevicePolicyManager.PasswordComplexity;
25 import android.app.admin.PasswordMetrics;
26 import android.content.Context;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.VisibleForTesting;
32 
33 import com.android.internal.widget.LockPatternUtils;
34 import com.android.settings.R;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * A controller for ChooseLockGeneric, and other similar classes which shows a list of possible
41  * screen lock types for the user to choose from. This is the main place where different
42  * restrictions on allowed screen lock types are aggregated in Settings.
43  *
44  * Each screen lock type has two states: whether it is visible and whether it is enabled.
45  * Visibility is affected by things like resource configs, whether it's for a managed profile,
46  * or whether the caller allows it or not. This is determined by
47  * {@link #isScreenLockVisible(ScreenLockType)}. For visible screen lock types, they can be disabled
48  * by a combination of admin policies and request from the calling app, which is determined by
49  * {@link #isScreenLockEnabled(ScreenLockType}.
50  */
51 
52 public class ChooseLockGenericController {
53 
54     private final Context mContext;
55     private final int mUserId;
56     private final boolean mHideInsecureScreenLockTypes;
57     @PasswordComplexity private final int mAppRequestedMinComplexity;
58     private final boolean mDevicePasswordRequirementOnly;
59     private final int mUnificationProfileId;
60     private final ManagedLockPasswordProvider mManagedPasswordProvider;
61     private final LockPatternUtils mLockPatternUtils;
62 
ChooseLockGenericController(Context context, int userId, ManagedLockPasswordProvider managedPasswordProvider, LockPatternUtils lockPatternUtils, boolean hideInsecureScreenLockTypes, int appRequestedMinComplexity, boolean devicePasswordRequirementOnly, int unificationProfileId)63     public ChooseLockGenericController(Context context, int userId,
64             ManagedLockPasswordProvider managedPasswordProvider, LockPatternUtils lockPatternUtils,
65             boolean hideInsecureScreenLockTypes, int appRequestedMinComplexity,
66             boolean devicePasswordRequirementOnly, int unificationProfileId) {
67         mContext = context;
68         mUserId = userId;
69         mManagedPasswordProvider = managedPasswordProvider;
70         mLockPatternUtils = lockPatternUtils;
71         mHideInsecureScreenLockTypes = hideInsecureScreenLockTypes;
72         mAppRequestedMinComplexity = appRequestedMinComplexity;
73         mDevicePasswordRequirementOnly = devicePasswordRequirementOnly;
74         mUnificationProfileId = unificationProfileId;
75     }
76 
77     /** Builder class for {@link ChooseLockGenericController} */
78     public static class Builder {
79         private final Context mContext;
80         private final int mUserId;
81         private final ManagedLockPasswordProvider mManagedPasswordProvider;
82         private final LockPatternUtils mLockPatternUtils;
83 
84         private boolean mHideInsecureScreenLockTypes = false;
85         @PasswordComplexity private int mAppRequestedMinComplexity = PASSWORD_COMPLEXITY_NONE;
86         private boolean mDevicePasswordRequirementOnly = false;
87         private int mUnificationProfileId = UserHandle.USER_NULL;
88 
Builder(Context context, int userId)89         public Builder(Context context, int userId) {
90             this(context, userId, new LockPatternUtils(context));
91         }
92 
Builder(Context context, int userId, LockPatternUtils lockPatternUtils)93         public Builder(Context context, int userId,
94                 LockPatternUtils lockPatternUtils) {
95             this(
96                     context,
97                     userId,
98                     ManagedLockPasswordProvider.get(context, userId),
99                     lockPatternUtils);
100         }
101 
102         @VisibleForTesting
Builder( Context context, int userId, ManagedLockPasswordProvider managedLockPasswordProvider, LockPatternUtils lockPatternUtils)103         Builder(
104                 Context context,
105                 int userId,
106                 ManagedLockPasswordProvider managedLockPasswordProvider,
107                 LockPatternUtils lockPatternUtils) {
108             mContext = context;
109             mUserId = userId;
110             mManagedPasswordProvider = managedLockPasswordProvider;
111             mLockPatternUtils = lockPatternUtils;
112         }
113         /**
114          * Sets the password complexity requested by the calling app via
115          * {@link android.app.admin.DevicePolicyManager#EXTRA_PASSWORD_COMPLEXITY}.
116          */
setAppRequestedMinComplexity(int complexity)117         public Builder setAppRequestedMinComplexity(int complexity) {
118             mAppRequestedMinComplexity = complexity;
119             return this;
120         }
121 
122         /**
123          * Sets whether the enrolment flow should discard any password policies originating from the
124          * work profile, even if the work profile currently has unified challenge. This can be
125          * requested by the calling app via
126          * {@link android.app.admin.DevicePolicyManager#EXTRA_DEVICE_PASSWORD_REQUIREMENT_ONLY}.
127          */
setEnforceDevicePasswordRequirementOnly(boolean deviceOnly)128         public Builder setEnforceDevicePasswordRequirementOnly(boolean deviceOnly) {
129             mDevicePasswordRequirementOnly = deviceOnly;
130             return this;
131         }
132 
133         /**
134          * Sets the user ID of any profile whose work challenge should be unified at the end of this
135          * enrolment flow. This will lead to all password policies from that profile to be taken
136          * into consideration by this class, so that we are enrolling a compliant password. This is
137          * because once unified, the profile's password policy will be enforced on the new
138          * credential.
139          */
setProfileToUnify(int profileId)140         public Builder setProfileToUnify(int profileId) {
141             mUnificationProfileId = profileId;
142             return this;
143         }
144 
145         /**
146          * Sets whether insecure screen lock types (NONE and SWIPE) should be hidden in the UI.
147          */
setHideInsecureScreenLockTypes(boolean hide)148         public Builder setHideInsecureScreenLockTypes(boolean hide) {
149             mHideInsecureScreenLockTypes = hide;
150             return this;
151         }
152 
153         /** Creates {@link ChooseLockGenericController} instance. */
build()154         public ChooseLockGenericController build() {
155             return new ChooseLockGenericController(mContext, mUserId, mManagedPasswordProvider,
156                     mLockPatternUtils, mHideInsecureScreenLockTypes, mAppRequestedMinComplexity,
157                     mDevicePasswordRequirementOnly, mUnificationProfileId);
158         }
159     }
160 
161     /**
162      * Returns whether the given screen lock type should be visible in the given context.
163      */
isScreenLockVisible(ScreenLockType type)164     public boolean isScreenLockVisible(ScreenLockType type) {
165         final boolean managedProfile = mContext.getSystemService(UserManager.class)
166                 .isManagedProfile(mUserId);
167         switch (type) {
168             case NONE:
169                 return !mHideInsecureScreenLockTypes
170                     && !mContext.getResources().getBoolean(R.bool.config_hide_none_security_option)
171                     && !managedProfile; // Profiles should use unified challenge instead.
172             case SWIPE:
173                 return !mHideInsecureScreenLockTypes
174                     && !mContext.getResources().getBoolean(R.bool.config_hide_swipe_security_option)
175                     && !managedProfile; // Swipe doesn't make sense for profiles.
176             case MANAGED:
177                 return mManagedPasswordProvider.isManagedPasswordChoosable();
178             case PIN:
179             case PATTERN:
180             case PASSWORD:
181                 // Hide the secure lock screen options if the device doesn't support the secure lock
182                 // screen feature.
183                 return mLockPatternUtils.hasSecureLockScreen();
184         }
185         return true;
186     }
187 
188     /**
189      * Whether screen lock with {@code type} should be enabled assuming all relevant password
190      * requirements. The lock's visibility ({@link #isScreenLockVisible}) is not considered here.
191      */
isScreenLockEnabled(ScreenLockType type)192     public boolean isScreenLockEnabled(ScreenLockType type) {
193         return !mLockPatternUtils.isCredentialsDisabledForUser(mUserId)
194                 && type.maxQuality >= upgradeQuality(PASSWORD_QUALITY_UNSPECIFIED);
195     }
196 
197     /**
198      * Increases the given quality to be as high as the combined quality from all relevant
199      * password requirements.
200      */
201     // TODO(b/142781408): convert from quality to credential type once PIN is supported.
upgradeQuality(int quality)202     public int upgradeQuality(int quality) {
203         return Math.max(quality,
204                 Math.max(
205                         LockPatternUtils.credentialTypeToPasswordQuality(
206                                 getAggregatedPasswordMetrics().credType),
207                         PasswordMetrics.complexityLevelToMinQuality(
208                                 getAggregatedPasswordComplexity())
209                 )
210         );
211     }
212 
213     /**
214      * User friendly title for the given screen lock type.
215      */
getTitle(ScreenLockType type)216     public CharSequence getTitle(ScreenLockType type) {
217         switch (type) {
218             case NONE:
219                 return mContext.getText(R.string.unlock_set_unlock_off_title);
220             case SWIPE:
221                 return mContext.getText(R.string.unlock_set_unlock_none_title);
222             case PATTERN:
223                 return mContext.getText(R.string.unlock_set_unlock_pattern_title);
224             case PIN:
225                 return mContext.getText(R.string.unlock_set_unlock_pin_title);
226             case PASSWORD:
227                 return mContext.getText(R.string.unlock_set_unlock_password_title);
228             case MANAGED:
229                 return mManagedPasswordProvider.getPickerOptionTitle(false);
230         }
231         return null;
232     }
233 
234     /**
235      * Gets a list of screen lock types that should be visible for the given quality. The returned
236      * list is ordered in the natural order of the enum (the order those enums were defined). Screen
237      * locks disabled by password policy will not be returned.
238      */
239     @NonNull
getVisibleAndEnabledScreenLockTypes()240     public List<ScreenLockType> getVisibleAndEnabledScreenLockTypes() {
241         List<ScreenLockType> locks = new ArrayList<>();
242         // EnumSet's iterator guarantees the natural order of the enums
243         for (ScreenLockType lock : ScreenLockType.values()) {
244             if (isScreenLockVisible(lock) && isScreenLockEnabled(lock)) {
245                 locks.add(lock);
246             }
247         }
248         return locks;
249     }
250 
251     /**
252      * Returns the combined password metrics from all relevant policies which affects the current
253      * user. Normally password policies set on the current user's work profile instance will be
254      * taken into consideration here iff the work profile doesn't have its own work challenge.
255      * By setting {@link #mUnificationProfileId}, the work profile's password policy will always
256      * be combined here. Alternatively, by setting {@link #mDevicePasswordRequirementOnly}, its
257      * password policy will always be disregarded here.
258      */
getAggregatedPasswordMetrics()259     public PasswordMetrics getAggregatedPasswordMetrics() {
260         PasswordMetrics metrics = mLockPatternUtils.getRequestedPasswordMetrics(mUserId,
261                 mDevicePasswordRequirementOnly);
262         if (mUnificationProfileId != UserHandle.USER_NULL) {
263             metrics.maxWith(mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId));
264         }
265         return metrics;
266     }
267 
268     /**
269      * Returns the combined password complexity from all relevant policies which affects the current
270      * user. The same logic of handling work profile password policies as
271      * {@link #getAggregatedPasswordMetrics} applies here.
272      */
getAggregatedPasswordComplexity()273     public int getAggregatedPasswordComplexity() {
274         int complexity = Math.max(mAppRequestedMinComplexity,
275                 mLockPatternUtils.getRequestedPasswordComplexity(
276                         mUserId, mDevicePasswordRequirementOnly));
277         if (mUnificationProfileId != UserHandle.USER_NULL) {
278             complexity = Math.max(complexity,
279                     mLockPatternUtils.getRequestedPasswordComplexity(mUnificationProfileId));
280         }
281         return complexity;
282     }
283 
284     /**
285      * Returns whether any screen lock type has been disabled only due to password policy
286      * from the admin. Will return {@code false} if the restriction is purely due to calling
287      * app's request.
288      */
isScreenLockRestrictedByAdmin()289     public boolean isScreenLockRestrictedByAdmin() {
290         return getAggregatedPasswordMetrics().credType != CREDENTIAL_TYPE_NONE
291                 || isComplexityProvidedByAdmin();
292     }
293 
294     /**
295      * Returns whether the aggregated password complexity is non-zero and comes from
296      * admin policy.
297      */
isComplexityProvidedByAdmin()298     public boolean isComplexityProvidedByAdmin() {
299         final int aggregatedComplexity = getAggregatedPasswordComplexity();
300         return aggregatedComplexity > mAppRequestedMinComplexity
301                 && aggregatedComplexity > PASSWORD_COMPLEXITY_NONE;
302     }
303 }
304