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