• 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.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