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