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