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