• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 type.maxQuality >= upgradeQuality(PASSWORD_QUALITY_UNSPECIFIED);
194     }
195 
196     /**
197      * Increases the given quality to be as high as the combined quality from all relevant
198      * password requirements.
199      */
200     // TODO(b/142781408): convert from quality to credential type once PIN is supported.
upgradeQuality(int quality)201     public int upgradeQuality(int quality) {
202         return Math.max(quality,
203                 Math.max(
204                         LockPatternUtils.credentialTypeToPasswordQuality(
205                                 getAggregatedPasswordMetrics().credType),
206                         PasswordMetrics.complexityLevelToMinQuality(
207                                 getAggregatedPasswordComplexity())
208                 )
209         );
210     }
211 
212     /**
213      * User friendly title for the given screen lock type.
214      */
getTitle(ScreenLockType type)215     public CharSequence getTitle(ScreenLockType type) {
216         switch (type) {
217             case NONE:
218                 return mContext.getText(R.string.unlock_set_unlock_off_title);
219             case SWIPE:
220                 return mContext.getText(R.string.unlock_set_unlock_none_title);
221             case PATTERN:
222                 return mContext.getText(R.string.unlock_set_unlock_pattern_title);
223             case PIN:
224                 return mContext.getText(R.string.unlock_set_unlock_pin_title);
225             case PASSWORD:
226                 return mContext.getText(R.string.unlock_set_unlock_password_title);
227             case MANAGED:
228                 return mManagedPasswordProvider.getPickerOptionTitle(false);
229         }
230         return null;
231     }
232 
233     /**
234      * Gets a list of screen lock types that should be visible for the given quality. The returned
235      * list is ordered in the natural order of the enum (the order those enums were defined). Screen
236      * locks disabled by password policy will not be returned.
237      */
238     @NonNull
getVisibleAndEnabledScreenLockTypes()239     public List<ScreenLockType> getVisibleAndEnabledScreenLockTypes() {
240         List<ScreenLockType> locks = new ArrayList<>();
241         // EnumSet's iterator guarantees the natural order of the enums
242         for (ScreenLockType lock : ScreenLockType.values()) {
243             if (isScreenLockVisible(lock) && isScreenLockEnabled(lock)) {
244                 locks.add(lock);
245             }
246         }
247         return locks;
248     }
249 
250     /**
251      * Returns the combined password metrics from all relevant policies which affects the current
252      * user. Normally password policies set on the current user's work profile instance will be
253      * taken into consideration here iff the work profile doesn't have its own work challenge.
254      * By setting {@link #mUnificationProfileId}, the work profile's password policy will always
255      * be combined here. Alternatively, by setting {@link #mDevicePasswordRequirementOnly}, its
256      * password policy will always be disregarded here.
257      */
getAggregatedPasswordMetrics()258     public PasswordMetrics getAggregatedPasswordMetrics() {
259         PasswordMetrics metrics = mLockPatternUtils.getRequestedPasswordMetrics(mUserId,
260                 mDevicePasswordRequirementOnly);
261         if (mUnificationProfileId != UserHandle.USER_NULL) {
262             metrics.maxWith(mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId));
263         }
264         return metrics;
265     }
266 
267     /**
268      * Returns the combined password complexity from all relevant policies which affects the current
269      * user. The same logic of handling work profile password policies as
270      * {@link #getAggregatedPasswordMetrics} applies here.
271      */
getAggregatedPasswordComplexity()272     public int getAggregatedPasswordComplexity() {
273         int complexity = Math.max(mAppRequestedMinComplexity,
274                 mLockPatternUtils.getRequestedPasswordComplexity(
275                         mUserId, mDevicePasswordRequirementOnly));
276         if (mUnificationProfileId != UserHandle.USER_NULL) {
277             complexity = Math.max(complexity,
278                     mLockPatternUtils.getRequestedPasswordComplexity(mUnificationProfileId));
279         }
280         return complexity;
281     }
282 
283     /**
284      * Returns whether any screen lock type has been disabled only due to password policy
285      * from the admin. Will return {@code false} if the restriction is purely due to calling
286      * app's request.
287      */
isScreenLockRestrictedByAdmin()288     public boolean isScreenLockRestrictedByAdmin() {
289         return getAggregatedPasswordMetrics().credType != CREDENTIAL_TYPE_NONE
290                 || isComplexityProvidedByAdmin();
291     }
292 
293     /**
294      * Returns whether the aggregated password complexity is non-zero and comes from
295      * admin policy.
296      */
isComplexityProvidedByAdmin()297     public boolean isComplexityProvidedByAdmin() {
298         final int aggregatedComplexity = getAggregatedPasswordComplexity();
299         return aggregatedComplexity > mAppRequestedMinComplexity
300                 && aggregatedComplexity > PASSWORD_COMPLEXITY_NONE;
301     }
302 }
303