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