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 android.annotation.Nullable; 20 import android.app.Activity; 21 import android.app.Fragment; 22 import android.app.KeyguardManager; 23 import android.app.admin.DevicePolicyManager; 24 import android.content.Intent; 25 import android.content.IntentSender; 26 import android.os.Bundle; 27 import android.os.UserManager; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.internal.widget.LockPatternUtils; 31 import com.android.settings.SettingsActivity; 32 import com.android.settings.Utils; 33 import com.android.setupwizardlib.util.WizardManagerHelper; 34 35 public final class ChooseLockSettingsHelper { 36 37 public static final String EXTRA_KEY_TYPE = "type"; 38 public static final String EXTRA_KEY_PASSWORD = "password"; 39 public static final String EXTRA_KEY_RETURN_CREDENTIALS = "return_credentials"; 40 public static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge"; 41 public static final String EXTRA_KEY_CHALLENGE = "challenge"; 42 public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token"; 43 public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint"; 44 public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot"; 45 46 /** 47 * When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag 48 * controls if we relax the enforcement of 49 * {@link Utils#enforceSameOwner(android.content.Context, int)}. 50 */ 51 public static final String EXTRA_ALLOW_ANY_USER = "allow_any_user"; 52 53 @VisibleForTesting LockPatternUtils mLockPatternUtils; 54 private Activity mActivity; 55 private Fragment mFragment; 56 57 public ChooseLockSettingsHelper(Activity activity) { 58 mActivity = activity; 59 mLockPatternUtils = new LockPatternUtils(activity); 60 } 61 62 public ChooseLockSettingsHelper(Activity activity, Fragment fragment) { 63 this(activity); 64 mFragment = fragment; 65 } 66 67 public LockPatternUtils utils() { 68 return mLockPatternUtils; 69 } 70 71 /** 72 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 73 * 74 * @param title title of the confirmation screen; shown in the action bar 75 * @return true if one exists and we launched an activity to confirm it 76 * @see Activity#onActivityResult(int, int, android.content.Intent) 77 */ 78 public boolean launchConfirmationActivity(int request, CharSequence title) { 79 return launchConfirmationActivity(request, title, null, null, false, false); 80 } 81 82 /** 83 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 84 * 85 * @param title title of the confirmation screen; shown in the action bar 86 * @param returnCredentials if true, put credentials into intent. Note that if this is true, 87 * this can only be called internally. 88 * @return true if one exists and we launched an activity to confirm it 89 * @see Activity#onActivityResult(int, int, android.content.Intent) 90 */ 91 public boolean launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials) { 92 return launchConfirmationActivity(request, title, null, null, returnCredentials, false); 93 } 94 95 /** 96 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 97 * 98 * @param title title of the confirmation screen; shown in the action bar 99 * @param returnCredentials if true, put credentials into intent. Note that if this is true, 100 * this can only be called internally. 101 * @param userId The userId for whom the lock should be confirmed. 102 * @return true if one exists and we launched an activity to confirm it 103 * @see Activity#onActivityResult(int, int, android.content.Intent) 104 */ 105 public boolean launchConfirmationActivity(int request, CharSequence title, 106 boolean returnCredentials, int userId) { 107 return launchConfirmationActivity(request, title, null, null, 108 returnCredentials, false, false, 0, Utils.enforceSameOwner(mActivity, userId)); 109 } 110 111 /** 112 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 113 * 114 * @param title title of the confirmation screen; shown in the action bar 115 * @param header header of the confirmation screen; shown as large text 116 * @param description description of the confirmation screen 117 * @param returnCredentials if true, put credentials into intent. Note that if this is true, 118 * this can only be called internally. 119 * @param external specifies whether this activity is launched externally, meaning that it will 120 * get a dark theme, allow fingerprint authentication and it will forward 121 * activity result. 122 * @return true if one exists and we launched an activity to confirm it 123 * @see Activity#onActivityResult(int, int, android.content.Intent) 124 */ 125 boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 126 @Nullable CharSequence header, @Nullable CharSequence description, 127 boolean returnCredentials, boolean external) { 128 return launchConfirmationActivity(request, title, header, description, 129 returnCredentials, external, false, 0, Utils.getCredentialOwnerUserId(mActivity)); 130 } 131 132 /** 133 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 134 * 135 * @param title title of the confirmation screen; shown in the action bar 136 * @param header header of the confirmation screen; shown as large text 137 * @param description description of the confirmation screen 138 * @param returnCredentials if true, put credentials into intent. Note that if this is true, 139 * this can only be called internally. 140 * @param external specifies whether this activity is launched externally, meaning that it will 141 * get a dark theme, allow fingerprint authentication and it will forward 142 * activity result. 143 * @param userId The userId for whom the lock should be confirmed. 144 * @return true if one exists and we launched an activity to confirm it 145 * @see Activity#onActivityResult(int, int, android.content.Intent) 146 */ 147 boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 148 @Nullable CharSequence header, @Nullable CharSequence description, 149 boolean returnCredentials, boolean external, int userId) { 150 return launchConfirmationActivity(request, title, header, description, 151 returnCredentials, external, false, 0, Utils.enforceSameOwner(mActivity, userId)); 152 } 153 154 /** 155 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 156 * 157 * @param title title of the confirmation screen; shown in the action bar 158 * @param header header of the confirmation screen; shown as large text 159 * @param description description of the confirmation screen 160 * @param challenge a challenge to be verified against the device credential. 161 * @return true if one exists and we launched an activity to confirm it 162 * @see Activity#onActivityResult(int, int, android.content.Intent) 163 */ 164 public boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 165 @Nullable CharSequence header, @Nullable CharSequence description, 166 long challenge) { 167 return launchConfirmationActivity(request, title, header, description, 168 true, false, true, challenge, Utils.getCredentialOwnerUserId(mActivity)); 169 } 170 171 /** 172 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 173 * 174 * @param title title of the confirmation screen; shown in the action bar 175 * @param header header of the confirmation screen; shown as large text 176 * @param description description of the confirmation screen 177 * @param challenge a challenge to be verified against the device credential. 178 * @param userId The userId for whom the lock should be confirmed. 179 * @return true if one exists and we launched an activity to confirm it 180 * @see Activity#onActivityResult(int, int, android.content.Intent) 181 */ 182 public boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 183 @Nullable CharSequence header, @Nullable CharSequence description, 184 long challenge, int userId) { 185 return launchConfirmationActivity(request, title, header, description, 186 true, false, true, challenge, Utils.enforceSameOwner(mActivity, userId)); 187 } 188 189 /** 190 * If a pattern, password or PIN exists, prompt the user before allowing them to change it. 191 * 192 * @param title title of the confirmation screen; shown in the action bar 193 * @param header header of the confirmation screen; shown as large text 194 * @param description description of the confirmation screen 195 * @param external specifies whether this activity is launched externally, meaning that it will 196 * get a dark theme, allow fingerprint authentication and it will forward 197 * activity result. 198 * @param challenge a challenge to be verified against the device credential. 199 * @param userId The userId for whom the lock should be confirmed. 200 * @return true if one exists and we launched an activity to confirm it 201 * @see Activity#onActivityResult(int, int, android.content.Intent) 202 */ 203 public boolean launchConfirmationActivityWithExternalAndChallenge(int request, 204 @Nullable CharSequence title, @Nullable CharSequence header, 205 @Nullable CharSequence description, boolean external, long challenge, int userId) { 206 return launchConfirmationActivity(request, title, header, description, false, 207 external, true, challenge, Utils.enforceSameOwner(mActivity, userId)); 208 } 209 210 /** 211 * Variant that allows you to prompt for credentials of any user, including 212 * those which aren't associated with the current user. As an example, this 213 * is useful when unlocking the storage for secondary users. 214 */ 215 public boolean launchConfirmationActivityForAnyUser(int request, 216 @Nullable CharSequence title, @Nullable CharSequence header, 217 @Nullable CharSequence description, int userId) { 218 final Bundle extras = new Bundle(); 219 extras.putBoolean(EXTRA_ALLOW_ANY_USER, true); 220 return launchConfirmationActivity(request, title, header, description, false, 221 false, true, 0, userId, extras); 222 } 223 224 private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 225 @Nullable CharSequence header, @Nullable CharSequence description, 226 boolean returnCredentials, boolean external, boolean hasChallenge, 227 long challenge, int userId) { 228 return launchConfirmationActivity(request, title, header, description, returnCredentials, 229 external, hasChallenge, challenge, userId, null /* alternateButton */, null); 230 } 231 232 private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 233 @Nullable CharSequence header, @Nullable CharSequence description, 234 boolean returnCredentials, boolean external, boolean hasChallenge, 235 long challenge, int userId, Bundle extras) { 236 return launchConfirmationActivity(request, title, header, description, returnCredentials, 237 external, hasChallenge, challenge, userId, null /* alternateButton */, extras); 238 } 239 240 public boolean launchFrpConfirmationActivity(int request, @Nullable CharSequence header, 241 @Nullable CharSequence description, @Nullable CharSequence alternateButton) { 242 return launchConfirmationActivity(request, null /* title */, header, description, 243 false /* returnCredentials */, true /* external */, false /* hasChallenge */, 244 0 /* challenge */, LockPatternUtils.USER_FRP, alternateButton, null); 245 } 246 247 private boolean launchConfirmationActivity(int request, @Nullable CharSequence title, 248 @Nullable CharSequence header, @Nullable CharSequence description, 249 boolean returnCredentials, boolean external, boolean hasChallenge, 250 long challenge, int userId, @Nullable CharSequence alternateButton, Bundle extras) { 251 final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId); 252 boolean launched = false; 253 254 switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) { 255 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 256 launched = launchConfirmationActivity(request, title, header, description, 257 returnCredentials || hasChallenge 258 ? ConfirmLockPattern.InternalActivity.class 259 : ConfirmLockPattern.class, returnCredentials, external, 260 hasChallenge, challenge, userId, alternateButton, extras); 261 break; 262 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 263 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: 264 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 265 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 266 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 267 case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: 268 launched = launchConfirmationActivity(request, title, header, description, 269 returnCredentials || hasChallenge 270 ? ConfirmLockPassword.InternalActivity.class 271 : ConfirmLockPassword.class, returnCredentials, external, 272 hasChallenge, challenge, userId, alternateButton, extras); 273 break; 274 } 275 return launched; 276 } 277 278 private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header, 279 CharSequence message, Class<?> activityClass, boolean returnCredentials, 280 boolean external, boolean hasChallenge, long challenge, 281 int userId, @Nullable CharSequence alternateButton, Bundle extras) { 282 final Intent intent = new Intent(); 283 intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title); 284 intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header); 285 intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message); 286 intent.putExtra(ConfirmDeviceCredentialBaseFragment.ALLOW_FP_AUTHENTICATION, external); 287 // TODO: Remove dark theme and show_cancel_button options since they are no longer used 288 intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false); 289 intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, false); 290 intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external); 291 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials); 292 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, hasChallenge); 293 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge); 294 // we should never have a drawer when confirming device credentials. 295 intent.putExtra(SettingsActivity.EXTRA_HIDE_DRAWER, true); 296 intent.putExtra(Intent.EXTRA_USER_ID, userId); 297 intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton); 298 if (extras != null) { 299 intent.putExtras(extras); 300 } 301 intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName()); 302 if (external) { 303 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 304 if (mFragment != null) { 305 copyOptionalExtras(mFragment.getActivity().getIntent(), intent); 306 mFragment.startActivity(intent); 307 } else { 308 copyOptionalExtras(mActivity.getIntent(), intent); 309 mActivity.startActivity(intent); 310 } 311 } else { 312 if (mFragment != null) { 313 copyInternalExtras(mFragment.getActivity().getIntent(), intent); 314 mFragment.startActivityForResult(intent, request); 315 } else { 316 copyInternalExtras(mActivity.getIntent(), intent); 317 mActivity.startActivityForResult(intent, request); 318 } 319 } 320 return true; 321 } 322 323 private void copyOptionalExtras(Intent inIntent, Intent outIntent) { 324 IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT); 325 if (intentSender != null) { 326 outIntent.putExtra(Intent.EXTRA_INTENT, intentSender); 327 } 328 int taskId = inIntent.getIntExtra(Intent.EXTRA_TASK_ID, -1); 329 if (taskId != -1) { 330 outIntent.putExtra(Intent.EXTRA_TASK_ID, taskId); 331 } 332 // If we will launch another activity once credentials are confirmed, exclude from recents. 333 // This is a workaround to a framework bug where affinity is incorrect for activities 334 // that are started from a no display activity, as is ConfirmDeviceCredentialActivity. 335 // TODO: Remove once that bug is fixed. 336 if (intentSender != null || taskId != -1) { 337 outIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 338 outIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 339 } 340 } 341 342 private void copyInternalExtras(Intent inIntent, Intent outIntent) { 343 String theme = inIntent.getStringExtra(WizardManagerHelper.EXTRA_THEME); 344 if (theme != null) { 345 outIntent.putExtra(WizardManagerHelper.EXTRA_THEME, theme); 346 } 347 } 348 } 349