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