1 /* 2 * Copyright (C) 2010 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 com.android.settings.Utils.SETTINGS_PACKAGE_NAME; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.Activity; 24 import android.app.KeyguardManager; 25 import android.app.admin.DevicePolicyManager; 26 import android.content.Intent; 27 import android.content.IntentSender; 28 import android.os.UserManager; 29 import android.util.Log; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.fragment.app.Fragment; 33 34 import com.android.internal.widget.LockPatternUtils; 35 import com.android.settings.SetupWizardUtils; 36 import com.android.settings.Utils; 37 import com.android.settings.core.SettingsBaseActivity; 38 import com.android.settings.core.SubSettingLauncher; 39 import com.android.settingslib.transition.SettingsTransitionHelper; 40 41 import com.google.android.setupcompat.util.WizardManagerHelper; 42 43 public final class ChooseLockSettingsHelper { 44 45 private static final String TAG = "ChooseLockSettingsHelper"; 46 47 public static final String EXTRA_KEY_TYPE = "type"; 48 public static final String EXTRA_KEY_PASSWORD = "password"; 49 public static final String EXTRA_KEY_RETURN_CREDENTIALS = "return_credentials"; 50 // Force the verifyCredential path instead of checkCredential path. This will be removed 51 // after b/161956762 is resolved. 52 public static final String EXTRA_KEY_FORCE_VERIFY = "force_verify"; 53 // Gatekeeper HardwareAuthToken 54 public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token"; 55 // For the fingerprint-only path 56 public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint"; 57 // For the face-only path 58 public static final String EXTRA_KEY_FOR_FACE = "for_face"; 59 // For the paths where multiple biometric sensors exist 60 public static final String EXTRA_KEY_FOR_BIOMETRICS = "for_biometrics"; 61 public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot"; 62 public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only"; 63 public static final String EXTRA_KEY_REQUEST_GK_PW_HANDLE = "request_gk_pw_handle"; 64 // Gatekeeper password handle, which can subsequently be used to generate Gatekeeper 65 // HardwareAuthToken(s) via LockSettingsService#verifyGatekeeperPasswordHandle 66 public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle"; 67 68 /** 69 * When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are 70 * provided to ChooseLockGeneric as fragment arguments {@link SubSettingLauncher#setArguments}, 71 * at the end of the password change flow, the supplied profile user 72 * (EXTRA_KEY_UNIFICATION_PROFILE_ID) will be unified to its parent. The current profile 73 * password is supplied by EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL. 74 */ 75 public static final String EXTRA_KEY_UNIFICATION_PROFILE_ID = "unification_profile_id"; 76 public static final String EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL = 77 "unification_profile_credential"; 78 79 /** 80 * Intent extra for passing the requested min password complexity to later steps in the set new 81 * screen lock flow. 82 */ 83 public static final String EXTRA_KEY_REQUESTED_MIN_COMPLEXITY = "requested_min_complexity"; 84 85 /** 86 * Intent extra for passing the label of the calling app to later steps in the set new screen 87 * lock flow. 88 */ 89 public static final String EXTRA_KEY_CALLER_APP_NAME = "caller_app_name"; 90 91 /** 92 * Intent extra indicating that the calling app is an admin, such as a Device Adimn, Device 93 * Owner, or Profile Owner. 94 */ 95 public static final String EXTRA_KEY_IS_CALLING_APP_ADMIN = "is_calling_app_admin"; 96 97 /** 98 * When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag 99 * controls if we relax the enforcement of 100 * {@link Utils#enforceSameOwner(android.content.Context, int)}. 101 */ 102 public static final String EXTRA_KEY_ALLOW_ANY_USER = "allow_any_user"; 103 104 /** 105 * 106 */ 107 public static final String EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY = 108 "device_password_requirement_only"; 109 110 @VisibleForTesting @NonNull LockPatternUtils mLockPatternUtils; 111 @NonNull private final Activity mActivity; 112 @Nullable private final Fragment mFragment; 113 @NonNull private final Builder mBuilder; 114 ChooseLockSettingsHelper(@onNull Builder builder, @NonNull Activity activity, @Nullable Fragment fragment)115 private ChooseLockSettingsHelper(@NonNull Builder builder, @NonNull Activity activity, 116 @Nullable Fragment fragment) { 117 mBuilder = builder; 118 mActivity = activity; 119 mFragment = fragment; 120 mLockPatternUtils = new LockPatternUtils(activity); 121 } 122 123 public static class Builder { 124 @NonNull private final Activity mActivity; 125 @Nullable private Fragment mFragment; 126 127 private int mRequestCode; 128 @Nullable private CharSequence mTitle; 129 @Nullable private CharSequence mHeader; 130 @Nullable private CharSequence mDescription; 131 @Nullable private CharSequence mAlternateButton; 132 private boolean mReturnCredentials; 133 private boolean mExternal; 134 private boolean mForegroundOnly; 135 // ChooseLockSettingsHelper will determine the caller's userId if none provided. 136 private int mUserId; 137 private boolean mAllowAnyUserId; 138 private boolean mForceVerifyPath; 139 boolean mRequestGatekeeperPasswordHandle; 140 Builder(@onNull Activity activity)141 public Builder(@NonNull Activity activity) { 142 mActivity = activity; 143 mUserId = Utils.getCredentialOwnerUserId(mActivity); 144 } 145 Builder(@onNull Activity activity, @NonNull Fragment fragment)146 public Builder(@NonNull Activity activity, @NonNull Fragment fragment) { 147 this(activity); 148 mFragment = fragment; 149 } 150 151 /** 152 * @param requestCode for onActivityResult 153 */ setRequestCode(int requestCode)154 @NonNull public Builder setRequestCode(int requestCode) { 155 mRequestCode = requestCode; 156 return this; 157 } 158 159 /** 160 * @param title of the confirmation screen; shown in the action bar 161 */ setTitle(@ullable CharSequence title)162 @NonNull public Builder setTitle(@Nullable CharSequence title) { 163 mTitle = title; 164 return this; 165 } 166 167 /** 168 * @param header of the confirmation screen; shown as large text 169 */ setHeader(@ullable CharSequence header)170 @NonNull public Builder setHeader(@Nullable CharSequence header) { 171 mHeader = header; 172 return this; 173 } 174 175 /** 176 * @param description of the confirmation screen 177 */ setDescription(@ullable CharSequence description)178 @NonNull public Builder setDescription(@Nullable CharSequence description) { 179 mDescription = description; 180 return this; 181 } 182 183 /** 184 * @param alternateButton text for an alternate button 185 */ setAlternateButton(@ullable CharSequence alternateButton)186 @NonNull public Builder setAlternateButton(@Nullable CharSequence alternateButton) { 187 mAlternateButton = alternateButton; 188 return this; 189 } 190 191 /** 192 * @param returnCredentials if true, puts the following credentials into intent for 193 * onActivityResult with the following keys: 194 * {@link #EXTRA_KEY_TYPE}, {@link #EXTRA_KEY_PASSWORD}, 195 * {@link #EXTRA_KEY_CHALLENGE_TOKEN}, 196 * {@link #EXTRA_KEY_GK_PW_HANDLE} 197 * Note that if this is true, this can only be called internally. 198 * 199 * This should also generally be set if 200 * {@link #setRequestGatekeeperPasswordHandle(boolean)} is set. 201 */ setReturnCredentials(boolean returnCredentials)202 @NonNull public Builder setReturnCredentials(boolean returnCredentials) { 203 mReturnCredentials = returnCredentials; 204 return this; 205 } 206 207 /** 208 * @param userId for whom the credential should be confirmed. 209 */ setUserId(int userId)210 @NonNull public Builder setUserId(int userId) { 211 mUserId = userId; 212 return this; 213 } 214 215 /** 216 * @param allowAnyUserId Allows the caller to prompt for credentials of any user, including 217 * those which aren't associated with the current user. As an example, 218 * this is useful when unlocking the storage for secondary users. 219 */ setAllowAnyUserId(boolean allowAnyUserId)220 @NonNull public Builder setAllowAnyUserId(boolean allowAnyUserId) { 221 mAllowAnyUserId = allowAnyUserId; 222 return this; 223 } 224 225 /** 226 * @param external specifies whether this activity is launched externally, meaning that it 227 * will get a dark theme, allow biometric authentication, and it will 228 * forward the activity result. 229 */ setExternal(boolean external)230 @NonNull public Builder setExternal(boolean external) { 231 mExternal = external; 232 return this; 233 } 234 235 /** 236 * @param foregroundOnly if true, the confirmation activity will be finished if it loses 237 * foreground. 238 */ setForegroundOnly(boolean foregroundOnly)239 @NonNull public Builder setForegroundOnly(boolean foregroundOnly) { 240 mForegroundOnly = foregroundOnly; 241 return this; 242 } 243 244 /** 245 * @param forceVerifyPath Forces the VerifyCredential path instead of the CheckCredential 246 * path. This will be removed after b/161956762 is resolved. 247 */ setForceVerifyPath(boolean forceVerifyPath)248 @NonNull public Builder setForceVerifyPath(boolean forceVerifyPath) { 249 mForceVerifyPath = forceVerifyPath; 250 return this; 251 } 252 253 /** 254 * Requests that LockSettingsService return a handle to the Gatekeeper Password (instead of 255 * the Gatekeeper HAT). This allows us to use a single entry of the user's credential 256 * to create multiple Gatekeeper HATs containing distinct challenges via 257 * {@link LockPatternUtils#verifyGatekeeperPasswordHandle(long, long, int)}. 258 * 259 * Upon confirmation of the user's password, the Gatekeeper Password Handle will be returned 260 * via onActivityResult with the key being {@link #EXTRA_KEY_GK_PW_HANDLE}. 261 * @param requestGatekeeperPasswordHandle 262 */ setRequestGatekeeperPasswordHandle( boolean requestGatekeeperPasswordHandle)263 @NonNull public Builder setRequestGatekeeperPasswordHandle( 264 boolean requestGatekeeperPasswordHandle) { 265 mRequestGatekeeperPasswordHandle = requestGatekeeperPasswordHandle; 266 return this; 267 } 268 build()269 @NonNull public ChooseLockSettingsHelper build() { 270 if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP) { 271 Utils.enforceSameOwner(mActivity, mUserId); 272 } 273 274 if (mExternal && mReturnCredentials) { 275 throw new IllegalArgumentException("External and ReturnCredentials specified. " 276 + " External callers should never be allowed to receive credentials in" 277 + " onActivityResult"); 278 } 279 280 if (mRequestGatekeeperPasswordHandle && !mReturnCredentials) { 281 // HAT containing the signed challenge will not be available to the caller. 282 Log.w(TAG, "Requested gatekeeper password handle but not requesting" 283 + " ReturnCredentials. Are you sure this is what you want?"); 284 } 285 286 return new ChooseLockSettingsHelper(this, mActivity, mFragment); 287 } 288 show()289 public boolean show() { 290 return build().launch(); 291 } 292 } 293 294 /** 295 * If a PIN, Pattern, or Password exists, prompt the user to confirm it. 296 * @return true if the confirmation activity is shown (e.g. user has a credential set up) 297 */ launch()298 public boolean launch() { 299 return launchConfirmationActivity(mBuilder.mRequestCode, mBuilder.mTitle, mBuilder.mHeader, 300 mBuilder.mDescription, mBuilder.mReturnCredentials, mBuilder.mExternal, 301 mBuilder.mForceVerifyPath, mBuilder.mUserId, mBuilder.mAlternateButton, 302 mBuilder.mAllowAnyUserId, mBuilder.mForegroundOnly, 303 mBuilder.mRequestGatekeeperPasswordHandle); 304 } 305 launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external, boolean forceVerifyPath, int userId, @Nullable CharSequence alternateButton, boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle)306 private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 307 @Nullable CharSequence header, @Nullable CharSequence description, 308 boolean returnCredentials, boolean external, boolean forceVerifyPath, 309 int userId, @Nullable CharSequence alternateButton, boolean allowAnyUser, 310 boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) { 311 final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId); 312 boolean launched = false; 313 314 switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) { 315 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 316 launched = launchConfirmationActivity(request, title, header, description, 317 returnCredentials || forceVerifyPath 318 ? ConfirmLockPattern.InternalActivity.class 319 : ConfirmLockPattern.class, returnCredentials, external, 320 forceVerifyPath, userId, alternateButton, allowAnyUser, 321 foregroundOnly, requestGatekeeperPasswordHandle); 322 break; 323 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 324 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: 325 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 326 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 327 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 328 case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: 329 launched = launchConfirmationActivity(request, title, header, description, 330 returnCredentials || forceVerifyPath 331 ? ConfirmLockPassword.InternalActivity.class 332 : ConfirmLockPassword.class, returnCredentials, external, 333 forceVerifyPath, userId, alternateButton, allowAnyUser, 334 foregroundOnly, requestGatekeeperPasswordHandle); 335 break; 336 } 337 return launched; 338 } 339 launchConfirmationActivity(int request, CharSequence title, CharSequence header, CharSequence message, Class<?> activityClass, boolean returnCredentials, boolean external, boolean forceVerifyPath, int userId, @Nullable CharSequence alternateButton, boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle)340 private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header, 341 CharSequence message, Class<?> activityClass, boolean returnCredentials, 342 boolean external, boolean forceVerifyPath, int userId, 343 @Nullable CharSequence alternateButton, boolean allowAnyUser, 344 boolean foregroundOnly, boolean requestGatekeeperPasswordHandle) { 345 final Intent intent = new Intent(); 346 intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title); 347 intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header); 348 intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message); 349 // TODO: Remove dark theme and show_cancel_button options since they are no longer used 350 intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false); 351 intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, false); 352 intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external); 353 intent.putExtra(ConfirmDeviceCredentialBaseFragment.USE_FADE_ANIMATION, external); 354 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials); 355 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, forceVerifyPath); 356 intent.putExtra(Intent.EXTRA_USER_ID, userId); 357 intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton); 358 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, foregroundOnly); 359 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser); 360 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, 361 requestGatekeeperPasswordHandle); 362 363 intent.setClassName(SETTINGS_PACKAGE_NAME, activityClass.getName()); 364 intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, 365 SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE); 366 367 Intent inIntent = mFragment != null ? mFragment.getActivity().getIntent() : 368 mActivity.getIntent(); 369 copyInternalExtras(inIntent, intent); 370 if (external) { 371 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 372 copyOptionalExtras(inIntent, intent); 373 if (mFragment != null) { 374 mFragment.startActivity(intent); 375 } else { 376 mActivity.startActivity(intent); 377 } 378 } else { 379 if (mFragment != null) { 380 mFragment.startActivityForResult(intent, request); 381 } else { 382 mActivity.startActivityForResult(intent, request); 383 } 384 } 385 return true; 386 } 387 copyOptionalExtras(Intent inIntent, Intent outIntent)388 private void copyOptionalExtras(Intent inIntent, Intent outIntent) { 389 IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT); 390 if (intentSender != null) { 391 outIntent.putExtra(Intent.EXTRA_INTENT, intentSender); 392 } 393 int taskId = inIntent.getIntExtra(Intent.EXTRA_TASK_ID, -1); 394 if (taskId != -1) { 395 outIntent.putExtra(Intent.EXTRA_TASK_ID, taskId); 396 } 397 // If we will launch another activity once credentials are confirmed, exclude from recents. 398 // This is a workaround to a framework bug where affinity is incorrect for activities 399 // that are started from a no display activity, as is ConfirmDeviceCredentialActivity. 400 // TODO: Remove once that bug is fixed. 401 if (intentSender != null || taskId != -1) { 402 outIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 403 outIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 404 } 405 } 406 copyInternalExtras(Intent inIntent, Intent outIntent)407 private void copyInternalExtras(Intent inIntent, Intent outIntent) { 408 SetupWizardUtils.copySetupExtras(inIntent, outIntent); 409 String theme = inIntent.getStringExtra(WizardManagerHelper.EXTRA_THEME); 410 if (theme != null) { 411 outIntent.putExtra(WizardManagerHelper.EXTRA_THEME, theme); 412 } 413 } 414 } 415