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.app.Activity;
22 import android.app.ActivityOptions;
23 import android.app.KeyguardManager;
24 import android.app.RemoteLockscreenValidationSession;
25 import android.app.admin.DevicePolicyManager;
26 import android.content.ComponentName;
27 import android.content.Intent;
28 import android.content.IntentSender;
29 import android.os.Bundle;
30 import android.os.UserManager;
31 import android.util.Log;
32 
33 import androidx.activity.result.ActivityResultLauncher;
34 import androidx.annotation.NonNull;
35 import androidx.annotation.Nullable;
36 import androidx.annotation.VisibleForTesting;
37 import androidx.fragment.app.Fragment;
38 
39 import com.android.internal.widget.LockPatternUtils;
40 import com.android.settings.SetupWizardUtils;
41 import com.android.settings.Utils;
42 import com.android.settings.core.SettingsBaseActivity;
43 import com.android.settings.core.SubSettingLauncher;
44 import com.android.settingslib.transition.SettingsTransitionHelper;
45 
46 import com.google.android.setupcompat.util.WizardManagerHelper;
47 
48 import java.util.Optional;
49 
50 public final class ChooseLockSettingsHelper {
51 
52     private static final String TAG = "ChooseLockSettingsHelper";
53 
54     public static final String EXTRA_KEY_PASSWORD = "password";
55     public static final String EXTRA_KEY_RETURN_CREDENTIALS = "return_credentials";
56     // Force the verifyCredential path instead of checkCredential path. This will be removed
57     // after b/161956762 is resolved.
58     public static final String EXTRA_KEY_FORCE_VERIFY = "force_verify";
59     // Gatekeeper HardwareAuthToken
60     public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
61     // For the fingerprint-only path
62     public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
63     // For the face-only path
64     public static final String EXTRA_KEY_FOR_FACE = "for_face";
65     // For the paths where multiple biometric sensors exist
66     public static final String EXTRA_KEY_FOR_BIOMETRICS = "for_biometrics";
67     // To support fingerprint enrollment only and skip other biometric enrollments like face.
68     public static final String EXTRA_KEY_FINGERPRINT_ENROLLMENT_ONLY = "for_fingerprint_only";
69     // For the paths where setup biometrics in suw flow
70     public static final String EXTRA_KEY_IS_SUW = "is_suw";
71     public static final String EXTRA_KEY_FOREGROUND_ONLY = "foreground_only";
72     public static final String EXTRA_KEY_REQUEST_GK_PW_HANDLE = "request_gk_pw_handle";
73     // Gatekeeper password handle, which can subsequently be used to generate Gatekeeper
74     // HardwareAuthToken(s) via LockSettingsService#verifyGatekeeperPasswordHandle
75     public static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
76     public static final String EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW =
77             "request_write_repair_mode_pw";
78     public static final String EXTRA_KEY_WROTE_REPAIR_MODE_CREDENTIAL =
79             "wrote_repair_mode_credential";
80 
81     /**
82      * When EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL and EXTRA_KEY_UNIFICATION_PROFILE_ID are
83      * provided to ChooseLockGeneric as fragment arguments {@link SubSettingLauncher#setArguments},
84      * at the end of the password change flow, the supplied profile user
85      * (EXTRA_KEY_UNIFICATION_PROFILE_ID) will be unified to its parent. The current profile
86      * password is supplied by EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL.
87      */
88     public static final String EXTRA_KEY_UNIFICATION_PROFILE_ID = "unification_profile_id";
89     public static final String EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL =
90             "unification_profile_credential";
91 
92     /**
93      * Intent extra for passing the requested min password complexity to later steps in the set new
94      * screen lock flow.
95      */
96     public static final String EXTRA_KEY_REQUESTED_MIN_COMPLEXITY = "requested_min_complexity";
97 
98     /**
99      * Intent extra for passing the label of the calling app to later steps in the set new screen
100      * lock flow.
101      */
102     public static final String EXTRA_KEY_CALLER_APP_NAME = "caller_app_name";
103 
104     /**
105      * Intent extra indicating that the calling app is an admin, such as a Device Adimn, Device
106      * Owner, or Profile Owner.
107      */
108     public static final String EXTRA_KEY_IS_CALLING_APP_ADMIN = "is_calling_app_admin";
109 
110     /**
111      * When invoked via {@link ConfirmLockPassword.InternalActivity}, this flag
112      * controls if we relax the enforcement of
113      * {@link Utils#enforceSameOwner(android.content.Context, int)}.
114      */
115     public static final String EXTRA_KEY_ALLOW_ANY_USER = "allow_any_user";
116 
117     /**
118      *
119      */
120     public static final String EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY =
121             "device_password_requirement_only";
122 
123     /** Intent extra for passing the screen title resource ID to show in the set lock screen. */
124     public static final String EXTRA_KEY_CHOOSE_LOCK_SCREEN_TITLE =
125             "choose_lock_setup_screen_title";
126 
127     /** Intent extra for passing the description resource ID to show in the set lock screen. */
128     public static final String EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION =
129             "choose_lock_setup_screen_description";
130 
131     @VisibleForTesting @NonNull LockPatternUtils mLockPatternUtils;
132     @NonNull private final Activity mActivity;
133     @Nullable private final Fragment mFragment;
134     @Nullable private final ActivityResultLauncher mActivityResultLauncher;
135     @NonNull private final Builder mBuilder;
136 
ChooseLockSettingsHelper(@onNull Builder builder, @NonNull Activity activity, @Nullable Fragment fragment, @Nullable ActivityResultLauncher activityResultLauncher)137     private ChooseLockSettingsHelper(@NonNull Builder builder, @NonNull Activity activity,
138             @Nullable Fragment fragment,
139             @Nullable ActivityResultLauncher activityResultLauncher) {
140         mBuilder = builder;
141         mActivity = activity;
142         mFragment = fragment;
143         mActivityResultLauncher = activityResultLauncher;
144         mLockPatternUtils = new LockPatternUtils(activity);
145     }
146 
147     public static class Builder {
148         @NonNull private final Activity mActivity;
149         @Nullable private Fragment mFragment;
150         @Nullable private ActivityResultLauncher mActivityResultLauncher;
151 
152         private int mRequestCode;
153         @Nullable private CharSequence mTitle;
154         @Nullable private CharSequence mHeader;
155         @Nullable private CharSequence mDescription;
156         @Nullable private CharSequence mAlternateButton;
157         @Nullable private CharSequence mCheckBoxLabel;
158         private boolean mReturnCredentials;
159         private boolean mExternal;
160         private boolean mForegroundOnly;
161         // ChooseLockSettingsHelper will determine the caller's userId if none provided.
162         private int mUserId;
163         private boolean mAllowAnyUserId;
164         private boolean mForceVerifyPath;
165         private boolean mRemoteLockscreenValidation;
166         @Nullable private RemoteLockscreenValidationSession mRemoteLockscreenValidationSession;
167         @Nullable private ComponentName mRemoteLockscreenValidationServiceComponent;
168         private boolean mRequestGatekeeperPasswordHandle;
169         private boolean mRequestWriteRepairModePassword;
170         private boolean mTaskOverlay;
171 
Builder(@onNull Activity activity)172         public Builder(@NonNull Activity activity) {
173             mActivity = activity;
174             mUserId = Utils.getCredentialOwnerUserId(mActivity);
175         }
176 
Builder(@onNull Activity activity, @NonNull Fragment fragment)177         public Builder(@NonNull Activity activity, @NonNull Fragment fragment) {
178             this(activity);
179             mFragment = fragment;
180         }
181 
182         /**
183          * @param requestCode for onActivityResult
184          */
setRequestCode(int requestCode)185         @NonNull public Builder setRequestCode(int requestCode) {
186             mRequestCode = requestCode;
187             return this;
188         }
189 
190         /**
191          * @param title of the confirmation screen; shown in the action bar
192          */
setTitle(@ullable CharSequence title)193         @NonNull public Builder setTitle(@Nullable CharSequence title) {
194             mTitle = title;
195             return this;
196         }
197 
198         /**
199          * @param header of the confirmation screen; shown as large text
200          */
setHeader(@ullable CharSequence header)201         @NonNull public Builder setHeader(@Nullable CharSequence header) {
202             mHeader = header;
203             return this;
204         }
205 
206         /**
207          * @param description of the confirmation screen
208          */
setDescription(@ullable CharSequence description)209         @NonNull public Builder setDescription(@Nullable CharSequence description) {
210             mDescription = description;
211             return this;
212         }
213 
214         /**
215          * @param alternateButton text for an alternate button
216          */
setAlternateButton(@ullable CharSequence alternateButton)217         @NonNull public Builder setAlternateButton(@Nullable CharSequence alternateButton) {
218             mAlternateButton = alternateButton;
219             return this;
220         }
221 
222         /**
223          * @param checkboxLabel text for the checkbox
224          */
225         @NonNull
setCheckboxLabel(@ullable CharSequence checkboxLabel)226         public Builder setCheckboxLabel(@Nullable CharSequence checkboxLabel) {
227             mCheckBoxLabel = checkboxLabel;
228             return this;
229         }
230 
231         /**
232          * @param returnCredentials if true, puts the following credentials into intent for
233          *                          onActivityResult with the following keys:
234          *                          {@link #EXTRA_KEY_PASSWORD},
235          *                          {@link #EXTRA_KEY_CHALLENGE_TOKEN},
236          *                          {@link #EXTRA_KEY_GK_PW_HANDLE}
237          *                          Note that if this is true, this can only be called internally.
238          *
239          *                          This should also generally be set if
240          *                          {@link #setRequestGatekeeperPasswordHandle(boolean)} is set.
241          */
setReturnCredentials(boolean returnCredentials)242         @NonNull public Builder setReturnCredentials(boolean returnCredentials) {
243             mReturnCredentials = returnCredentials;
244             return this;
245         }
246 
247         /**
248          * @param userId for whom the credential should be confirmed.
249          */
setUserId(int userId)250         @NonNull public Builder setUserId(int userId) {
251             mUserId = userId;
252             return this;
253         }
254 
255         /**
256          * @param allowAnyUserId Allows the caller to prompt for credentials of any user, including
257          *                       those which aren't associated with the current user. As an example,
258          *                       this is useful when unlocking the storage for secondary users.
259          */
setAllowAnyUserId(boolean allowAnyUserId)260         @NonNull public Builder setAllowAnyUserId(boolean allowAnyUserId) {
261             mAllowAnyUserId = allowAnyUserId;
262             return this;
263         }
264 
265         /**
266          * @param external specifies whether this activity is launched externally, meaning that it
267          *                 will get a dark theme, allow biometric authentication, and it will
268          *                 forward the activity result.
269          */
setExternal(boolean external)270         @NonNull public Builder setExternal(boolean external) {
271             mExternal = external;
272             return this;
273         }
274 
275         /**
276          * @param taskOverlay specifies whether the activity should be launched as a task overlay.
277          */
setTaskOverlay(boolean taskOverlay)278         @NonNull public Builder setTaskOverlay(boolean taskOverlay) {
279             mTaskOverlay = taskOverlay;
280             return this;
281         }
282 
283         /**
284          * @param foregroundOnly if true, the confirmation activity will be finished if it loses
285          *                       foreground.
286          */
setForegroundOnly(boolean foregroundOnly)287         @NonNull public Builder setForegroundOnly(boolean foregroundOnly) {
288             mForegroundOnly = foregroundOnly;
289             return this;
290         }
291 
292         /**
293          * @param forceVerifyPath Forces the VerifyCredential path instead of the CheckCredential
294          *                        path. This will be removed after b/161956762 is resolved.
295          */
setForceVerifyPath(boolean forceVerifyPath)296         @NonNull public Builder setForceVerifyPath(boolean forceVerifyPath) {
297             mForceVerifyPath = forceVerifyPath;
298             return this;
299         }
300 
301         /**
302          * @param isRemoteLockscreenValidation if true, remote device validation flow will be
303          *                                 started. {@link #setRemoteLockscreenValidationSession},
304          *                                 {@link #setRemoteLockscreenValidationServiceComponent}
305          *                                 must also be used to set the required data.
306          */
setRemoteLockscreenValidation( boolean isRemoteLockscreenValidation)307         @NonNull public Builder setRemoteLockscreenValidation(
308                 boolean isRemoteLockscreenValidation) {
309             mRemoteLockscreenValidation = isRemoteLockscreenValidation;
310             return this;
311         }
312 
313         /**
314          * @param remoteLockscreenValidationSession contains information necessary to perform remote
315          *                                         lockscreen validation such as the remote device's
316          *                                         lockscreen type, public key to be used for
317          *                                         encryption, and remaining attempts.
318          */
setRemoteLockscreenValidationSession( RemoteLockscreenValidationSession remoteLockscreenValidationSession)319         @NonNull public Builder setRemoteLockscreenValidationSession(
320                 RemoteLockscreenValidationSession remoteLockscreenValidationSession) {
321             mRemoteLockscreenValidationSession = remoteLockscreenValidationSession;
322             return this;
323         }
324 
325         /**
326          * @param remoteLockscreenValidationServiceComponent the {@link ComponentName} of the
327          * {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
328          * that will be used to validate the lockscreen guess.
329          */
setRemoteLockscreenValidationServiceComponent( ComponentName remoteLockscreenValidationServiceComponent)330         @NonNull public Builder setRemoteLockscreenValidationServiceComponent(
331                 ComponentName remoteLockscreenValidationServiceComponent) {
332             mRemoteLockscreenValidationServiceComponent =
333                     remoteLockscreenValidationServiceComponent;
334             return this;
335         }
336 
337         /**
338          * Requests that LockSettingsService return a handle to the Gatekeeper Password (instead of
339          * the Gatekeeper HAT). This allows us to use a single entry of the user's credential
340          * to create multiple Gatekeeper HATs containing distinct challenges via
341          * {@link LockPatternUtils#verifyGatekeeperPasswordHandle(long, long, int)}.
342          *
343          * Upon confirmation of the user's password, the Gatekeeper Password Handle will be returned
344          * via onActivityResult with the key being {@link #EXTRA_KEY_GK_PW_HANDLE}.
345          * @param requestGatekeeperPasswordHandle
346          */
setRequestGatekeeperPasswordHandle( boolean requestGatekeeperPasswordHandle)347         @NonNull public Builder setRequestGatekeeperPasswordHandle(
348                 boolean requestGatekeeperPasswordHandle) {
349             mRequestGatekeeperPasswordHandle = requestGatekeeperPasswordHandle;
350             return this;
351         }
352 
353         /**
354          * @param requestWriteRepairModePassword Set {@code true} to request that
355          * LockSettingsService writes the password data to the repair mode file after the user
356          * credential is verified successfully.
357          */
setRequestWriteRepairModePassword( boolean requestWriteRepairModePassword)358         @NonNull public Builder setRequestWriteRepairModePassword(
359                 boolean requestWriteRepairModePassword) {
360             mRequestWriteRepairModePassword = requestWriteRepairModePassword;
361             return this;
362         }
363 
364         /**
365          * Support of ActivityResultLauncher.
366          *
367          * Which allowing the launch operation be controlled externally.
368          * @param activityResultLauncher a launcher previously prepared.
369          */
setActivityResultLauncher( ActivityResultLauncher activityResultLauncher)370         @NonNull public Builder setActivityResultLauncher(
371                 ActivityResultLauncher activityResultLauncher) {
372             mActivityResultLauncher = activityResultLauncher;
373             return this;
374         }
375 
build()376         @NonNull public ChooseLockSettingsHelper build() {
377             if (!mAllowAnyUserId && mUserId != LockPatternUtils.USER_FRP
378                     && mUserId != LockPatternUtils.USER_REPAIR_MODE) {
379                 Utils.enforceSameOwner(mActivity, mUserId);
380             }
381 
382             if (mExternal && mReturnCredentials && !mRemoteLockscreenValidation) {
383                 throw new IllegalArgumentException("External and ReturnCredentials specified. "
384                         + " External callers should never be allowed to receive credentials in"
385                         + " onActivityResult");
386             }
387 
388             if (mRequestGatekeeperPasswordHandle && !mReturnCredentials) {
389                 // HAT containing the signed challenge will not be available to the caller.
390                 Log.w(TAG, "Requested gatekeeper password handle but not requesting"
391                         + " ReturnCredentials. Are you sure this is what you want?");
392             }
393 
394             return new ChooseLockSettingsHelper(this, mActivity, mFragment,
395                     mActivityResultLauncher);
396         }
397 
show()398         public boolean show() {
399             return build().launch();
400         }
401     }
402 
403     /**
404      * If a PIN, Pattern, or Password exists, prompt the user to confirm it.
405      * @return true if the confirmation activity is shown (e.g. user has a credential set up)
406      */
launch()407     public boolean launch() {
408         return launchConfirmationActivity(mBuilder.mRequestCode, mBuilder.mTitle, mBuilder.mHeader,
409                 mBuilder.mDescription, mBuilder.mReturnCredentials, mBuilder.mExternal,
410                 mBuilder.mForceVerifyPath, mBuilder.mUserId, mBuilder.mAlternateButton,
411                 mBuilder.mCheckBoxLabel, mBuilder.mRemoteLockscreenValidation,
412                 mBuilder.mRemoteLockscreenValidationSession,
413                 mBuilder.mRemoteLockscreenValidationServiceComponent, mBuilder.mAllowAnyUserId,
414                 mBuilder.mForegroundOnly, mBuilder.mRequestGatekeeperPasswordHandle,
415                 mBuilder.mRequestWriteRepairModePassword, mBuilder.mTaskOverlay);
416     }
417 
launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external, boolean forceVerifyPath, int userId, @Nullable CharSequence alternateButton, @Nullable CharSequence checkboxLabel, boolean remoteLockscreenValidation, @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession, @Nullable ComponentName remoteLockscreenValidationServiceComponent, boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle, boolean requestWriteRepairModePassword, boolean taskOverlay)418     private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
419             @Nullable CharSequence header, @Nullable CharSequence description,
420             boolean returnCredentials, boolean external, boolean forceVerifyPath,
421             int userId, @Nullable CharSequence alternateButton,
422             @Nullable CharSequence checkboxLabel, boolean remoteLockscreenValidation,
423             @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession,
424             @Nullable ComponentName remoteLockscreenValidationServiceComponent,
425             boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle,
426             boolean requestWriteRepairModePassword, boolean taskOverlay) {
427         Optional<Class<?>> activityClass = determineAppropriateActivityClass(
428                 returnCredentials, forceVerifyPath, userId, remoteLockscreenValidationSession);
429         if (activityClass.isEmpty()) {
430             return false;
431         }
432 
433         return launchConfirmationActivity(request, title, header, description, activityClass.get(),
434                 returnCredentials, external, forceVerifyPath, userId, alternateButton,
435                 checkboxLabel, remoteLockscreenValidation, remoteLockscreenValidationSession,
436                 remoteLockscreenValidationServiceComponent, allowAnyUser, foregroundOnly,
437                 requestGatekeeperPasswordHandle, requestWriteRepairModePassword, taskOverlay);
438     }
439 
launchConfirmationActivity(int request, CharSequence title, CharSequence header, CharSequence message, Class<?> activityClass, boolean returnCredentials, boolean external, boolean forceVerifyPath, int userId, @Nullable CharSequence alternateButton, @Nullable CharSequence checkbox, boolean remoteLockscreenValidation, @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession, @Nullable ComponentName remoteLockscreenValidationServiceComponent, boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle, boolean requestWriteRepairModePassword, boolean taskOverlay)440     private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
441             CharSequence message, Class<?> activityClass, boolean returnCredentials,
442             boolean external, boolean forceVerifyPath, int userId,
443             @Nullable CharSequence alternateButton, @Nullable CharSequence checkbox,
444             boolean remoteLockscreenValidation,
445             @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession,
446             @Nullable ComponentName remoteLockscreenValidationServiceComponent,
447             boolean allowAnyUser, boolean foregroundOnly, boolean requestGatekeeperPasswordHandle,
448             boolean requestWriteRepairModePassword, boolean taskOverlay) {
449         final Intent intent = new Intent();
450         intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
451         intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
452         intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message);
453         // TODO: Remove dark theme and show_cancel_button options since they are no longer used
454         intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false);
455         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, false);
456         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
457         intent.putExtra(ConfirmDeviceCredentialBaseFragment.USE_FADE_ANIMATION, external);
458         intent.putExtra(ConfirmDeviceCredentialBaseFragment.IS_REMOTE_LOCKSCREEN_VALIDATION,
459                 remoteLockscreenValidation);
460         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
461         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FORCE_VERIFY, forceVerifyPath);
462         intent.putExtra(Intent.EXTRA_USER_ID, userId);
463         intent.putExtra(KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL, alternateButton);
464         intent.putExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL, checkbox);
465         intent.putExtra(KeyguardManager.EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION,
466                 remoteLockscreenValidationSession);
467         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent);
468         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOREGROUND_ONLY, foregroundOnly);
469         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, allowAnyUser);
470         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE,
471                 requestGatekeeperPasswordHandle);
472         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW,
473                 requestWriteRepairModePassword);
474 
475         intent.setClassName(SETTINGS_PACKAGE_NAME, activityClass.getName());
476         intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
477                 SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
478 
479         Intent inIntent = mFragment != null ? mFragment.getActivity().getIntent() :
480                 mActivity.getIntent();
481         copyInternalExtras(inIntent, intent);
482         Bundle launchOptions = createLaunchOptions(taskOverlay);
483         if (external) {
484             intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
485             copyOptionalExtras(inIntent, intent);
486             if (mActivityResultLauncher != null) {
487                 mActivityResultLauncher.launch(intent);
488             } else if (mFragment != null) {
489                 mFragment.startActivity(intent, launchOptions);
490             } else {
491                 mActivity.startActivity(intent, launchOptions);
492             }
493         } else {
494             if (mActivityResultLauncher != null) {
495                 mActivityResultLauncher.launch(intent);
496             } else if (mFragment != null) {
497                 mFragment.startActivityForResult(intent, request, launchOptions);
498             } else {
499                 mActivity.startActivityForResult(intent, request, launchOptions);
500             }
501         }
502         return true;
503     }
504 
createLaunchOptions(boolean taskOverlay)505     private Bundle createLaunchOptions(boolean taskOverlay) {
506         if (!taskOverlay) {
507             return null;
508         }
509         ActivityOptions options = ActivityOptions.makeBasic();
510         options.setLaunchTaskId(mActivity.getTaskId());
511         options.setTaskOverlay(true /* taskOverlay */, true /* canResume */);
512         return options.toBundle();
513     }
514 
passwordQualityToLockTypes(int quality)515     private Optional<Integer> passwordQualityToLockTypes(int quality) {
516         switch (quality) {
517             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
518                 return Optional.of(KeyguardManager.PATTERN);
519             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
520             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
521                 return Optional.of(KeyguardManager.PIN);
522             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
523             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
524             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
525             case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
526                 return Optional.of(KeyguardManager.PASSWORD);
527         }
528         Log.e(TAG, String.format(
529                 "Cannot determine appropriate activity class for password quality %d",
530                 quality));
531         return Optional.empty();
532     }
533 
determineAppropriateActivityClass(boolean returnCredentials, boolean forceVerifyPath, int userId, @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession)534     private Optional<Class<?>> determineAppropriateActivityClass(boolean returnCredentials,
535             boolean forceVerifyPath, int userId,
536             @Nullable RemoteLockscreenValidationSession remoteLockscreenValidationSession) {
537         int lockType;
538         if (remoteLockscreenValidationSession != null) {
539             lockType = remoteLockscreenValidationSession.getLockType();
540         } else {
541             final int effectiveUserId = UserManager
542                     .get(mActivity).getCredentialOwnerProfile(userId);
543             Optional<Integer> lockTypeOptional = passwordQualityToLockTypes(
544                     mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId));
545             if (lockTypeOptional.isEmpty()) {
546                 return Optional.empty();
547             }
548             lockType = lockTypeOptional.get();
549         }
550 
551         switch (lockType) {
552             case KeyguardManager.PASSWORD:
553             case KeyguardManager.PIN:
554                 return Optional.of(returnCredentials || forceVerifyPath
555                         ? ConfirmLockPassword.InternalActivity.class
556                         : ConfirmLockPassword.class);
557             case KeyguardManager.PATTERN:
558                 return Optional.of(returnCredentials || forceVerifyPath
559                         ? ConfirmLockPattern.InternalActivity.class
560                         : ConfirmLockPattern.class);
561         }
562         Log.e(TAG, String.format("Cannot determine appropriate activity class for lock type %d",
563                 lockType));
564         return Optional.empty();
565     }
566 
copyOptionalExtras(Intent inIntent, Intent outIntent)567     private void copyOptionalExtras(Intent inIntent, Intent outIntent) {
568         IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
569         if (intentSender != null) {
570             outIntent.putExtra(Intent.EXTRA_INTENT, intentSender);
571         }
572         int taskId = inIntent.getIntExtra(Intent.EXTRA_TASK_ID, -1);
573         if (taskId != -1) {
574             outIntent.putExtra(Intent.EXTRA_TASK_ID, taskId);
575         }
576         // If we will launch another activity once credentials are confirmed, exclude from recents.
577         // This is a workaround to a framework bug where affinity is incorrect for activities
578         // that are started from a no display activity, as is ConfirmDeviceCredentialActivity.
579         // TODO: Remove once that bug is fixed.
580         if (intentSender != null || taskId != -1) {
581             outIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
582             outIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
583         }
584     }
585 
copyInternalExtras(Intent inIntent, Intent outIntent)586     private void copyInternalExtras(Intent inIntent, Intent outIntent) {
587         SetupWizardUtils.copySetupExtras(inIntent, outIntent);
588         String theme = inIntent.getStringExtra(WizardManagerHelper.EXTRA_THEME);
589         if (theme != null) {
590             outIntent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
591         }
592     }
593 }
594