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;
18 
19 import android.annotation.Nullable;
20 import android.app.Activity;
21 import android.app.Fragment;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.Intent;
24 import android.content.IntentSender;
25 import android.os.UserManager;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.internal.widget.LockPatternUtils;
29 
30 public final class ChooseLockSettingsHelper {
31 
32     static final String EXTRA_KEY_TYPE = "type";
33     static final String EXTRA_KEY_PASSWORD = "password";
34     public static final String EXTRA_KEY_RETURN_CREDENTIALS = "return_credentials";
35     public static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge";
36     public static final String EXTRA_KEY_CHALLENGE = "challenge";
37     public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
38     public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
39     public static final String EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT = "for_cred_req_boot";
40 
41 
42     @VisibleForTesting LockPatternUtils mLockPatternUtils;
43     private Activity mActivity;
44     private Fragment mFragment;
45 
ChooseLockSettingsHelper(Activity activity)46     public ChooseLockSettingsHelper(Activity activity) {
47         mActivity = activity;
48         mLockPatternUtils = new LockPatternUtils(activity);
49     }
50 
ChooseLockSettingsHelper(Activity activity, Fragment fragment)51     public ChooseLockSettingsHelper(Activity activity, Fragment fragment) {
52         this(activity);
53         mFragment = fragment;
54     }
55 
utils()56     public LockPatternUtils utils() {
57         return mLockPatternUtils;
58     }
59 
60     /**
61      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
62      *
63      * @param title title of the confirmation screen; shown in the action bar
64      * @return true if one exists and we launched an activity to confirm it
65      * @see Activity#onActivityResult(int, int, android.content.Intent)
66      */
launchConfirmationActivity(int request, CharSequence title)67     public boolean launchConfirmationActivity(int request, CharSequence title) {
68         return launchConfirmationActivity(request, title, null, null, false, false);
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      * @param returnCredentials if true, put credentials into intent. Note that if this is true,
76      *                          this can only be called internally.
77      * @return true if one exists and we launched an activity to confirm it
78      * @see Activity#onActivityResult(int, int, android.content.Intent)
79      */
launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials)80     boolean launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials) {
81         return launchConfirmationActivity(request, title, null, null, returnCredentials, false);
82     }
83 
84     /**
85      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
86      *
87      * @param title title of the confirmation screen; shown in the action bar
88      * @param returnCredentials if true, put credentials into intent. Note that if this is true,
89      *                          this can only be called internally.
90      * @param userId The userId for whom the lock should be confirmed.
91      * @return true if one exists and we launched an activity to confirm it
92      * @see Activity#onActivityResult(int, int, android.content.Intent)
93      */
launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials, int userId)94     public boolean launchConfirmationActivity(int request, CharSequence title,
95             boolean returnCredentials, int userId) {
96         return launchConfirmationActivity(request, title, null, null,
97                 returnCredentials, false, false, 0, Utils.enforceSameOwner(mActivity, userId));
98     }
99 
100     /**
101      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
102      *
103      * @param title title of the confirmation screen; shown in the action bar
104      * @param header header of the confirmation screen; shown as large text
105      * @param description description of the confirmation screen
106      * @param returnCredentials if true, put credentials into intent. Note that if this is true,
107      *                          this can only be called internally.
108      * @param external specifies whether this activity is launched externally, meaning that it will
109      *                 get a dark theme, allow fingerprint authentication and it will forward
110      *                 activity result.
111      * @return true if one exists and we launched an activity to confirm it
112      * @see Activity#onActivityResult(int, int, android.content.Intent)
113      */
launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external)114     boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
115             @Nullable CharSequence header, @Nullable CharSequence description,
116             boolean returnCredentials, boolean external) {
117         return launchConfirmationActivity(request, title, header, description,
118                 returnCredentials, external, false, 0, Utils.getCredentialOwnerUserId(mActivity));
119     }
120 
121     /**
122      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
123      *
124      * @param title title of the confirmation screen; shown in the action bar
125      * @param header header of the confirmation screen; shown as large text
126      * @param description description of the confirmation screen
127      * @param returnCredentials if true, put credentials into intent. Note that if this is true,
128      *                          this can only be called internally.
129      * @param external specifies whether this activity is launched externally, meaning that it will
130      *                 get a dark theme, allow fingerprint authentication and it will forward
131      *                 activity result.
132      * @param userId The userId for whom the lock should be confirmed.
133      * @return true if one exists and we launched an activity to confirm it
134      * @see Activity#onActivityResult(int, int, android.content.Intent)
135      */
launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external, int userId)136     boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
137             @Nullable CharSequence header, @Nullable CharSequence description,
138             boolean returnCredentials, boolean external, int userId) {
139         return launchConfirmationActivity(request, title, header, description,
140                 returnCredentials, external, false, 0, Utils.enforceSameOwner(mActivity, userId));
141     }
142 
143     /**
144      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
145      *
146      * @param title title of the confirmation screen; shown in the action bar
147      * @param header header of the confirmation screen; shown as large text
148      * @param description description of the confirmation screen
149      * @param challenge a challenge to be verified against the device credential.
150      * @return true if one exists and we launched an activity to confirm it
151      * @see Activity#onActivityResult(int, int, android.content.Intent)
152      */
launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, long challenge)153     public boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
154             @Nullable CharSequence header, @Nullable CharSequence description,
155             long challenge) {
156         return launchConfirmationActivity(request, title, header, description,
157                 true, false, true, challenge, Utils.getCredentialOwnerUserId(mActivity));
158     }
159 
160     /**
161      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
162      *
163      * @param title title of the confirmation screen; shown in the action bar
164      * @param header header of the confirmation screen; shown as large text
165      * @param description description of the confirmation screen
166      * @param challenge a challenge to be verified against the device credential.
167      * @param userId The userId for whom the lock should be confirmed.
168      * @return true if one exists and we launched an activity to confirm it
169      * @see Activity#onActivityResult(int, int, android.content.Intent)
170      */
launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, long challenge, int userId)171     public boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
172             @Nullable CharSequence header, @Nullable CharSequence description,
173             long challenge, int userId) {
174         return launchConfirmationActivity(request, title, header, description,
175                 true, false, true, challenge, Utils.enforceSameOwner(mActivity, userId));
176     }
177 
178     /**
179      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
180      *
181      * @param title title of the confirmation screen; shown in the action bar
182      * @param header header of the confirmation screen; shown as large text
183      * @param description description of the confirmation screen
184      * @param external specifies whether this activity is launched externally, meaning that it will
185      *                 get a dark theme, allow fingerprint authentication and it will forward
186      *                 activity result.
187      * @param challenge a challenge to be verified against the device credential.
188      * @param userId The userId for whom the lock should be confirmed.
189      * @return true if one exists and we launched an activity to confirm it
190      * @see Activity#onActivityResult(int, int, android.content.Intent)
191      */
launchConfirmationActivityWithExternalAndChallenge(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean external, long challenge, int userId)192     public boolean launchConfirmationActivityWithExternalAndChallenge(int request,
193             @Nullable CharSequence title, @Nullable CharSequence header,
194             @Nullable CharSequence description, boolean external, long challenge, int userId) {
195         return launchConfirmationActivity(request, title, header, description, false,
196                 external, true, challenge, Utils.enforceSameOwner(mActivity, userId));
197     }
198 
launchConfirmationActivity(int request, @Nullable CharSequence title, @Nullable CharSequence header, @Nullable CharSequence description, boolean returnCredentials, boolean external, boolean hasChallenge, long challenge, int userId)199     private boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
200             @Nullable CharSequence header, @Nullable CharSequence description,
201             boolean returnCredentials, boolean external, boolean hasChallenge,
202             long challenge, int userId) {
203         final int effectiveUserId = UserManager.get(mActivity).getCredentialOwnerProfile(userId);
204         boolean launched = false;
205 
206         switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) {
207             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
208                 launched = launchConfirmationActivity(request, title, header, description,
209                         returnCredentials || hasChallenge
210                                 ? ConfirmLockPattern.InternalActivity.class
211                                 : ConfirmLockPattern.class, returnCredentials, external,
212                                 hasChallenge, challenge, userId);
213                 break;
214             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
215             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
216             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
217             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
218             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
219             case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
220                 launched = launchConfirmationActivity(request, title, header, description,
221                         returnCredentials || hasChallenge
222                                 ? ConfirmLockPassword.InternalActivity.class
223                                 : ConfirmLockPassword.class, returnCredentials, external,
224                                 hasChallenge, challenge, userId);
225                 break;
226         }
227         return launched;
228     }
229 
launchConfirmationActivity(int request, CharSequence title, CharSequence header, CharSequence message, Class<?> activityClass, boolean returnCredentials, boolean external, boolean hasChallenge, long challenge, int userId)230     private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
231             CharSequence message, Class<?> activityClass, boolean returnCredentials,
232             boolean external, boolean hasChallenge, long challenge,
233             int userId) {
234         final Intent intent = new Intent();
235         intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
236         intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
237         intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message);
238         intent.putExtra(ConfirmDeviceCredentialBaseFragment.ALLOW_FP_AUTHENTICATION, external);
239         intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, external);
240         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, external);
241         intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
242         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, returnCredentials);
243         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, hasChallenge);
244         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
245         // we should never have a drawer when confirming device credentials.
246         intent.putExtra(SettingsActivity.EXTRA_HIDE_DRAWER, true);
247         intent.putExtra(Intent.EXTRA_USER_ID, userId);
248         intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName());
249         if (external) {
250             intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
251             if (mFragment != null) {
252                 copyOptionalExtras(mFragment.getActivity().getIntent(), intent);
253                 mFragment.startActivity(intent);
254             } else {
255                 copyOptionalExtras(mActivity.getIntent(), intent);
256                 mActivity.startActivity(intent);
257             }
258         } else {
259             if (mFragment != null) {
260                 mFragment.startActivityForResult(intent, request);
261             } else {
262                 mActivity.startActivityForResult(intent, request);
263             }
264         }
265         return true;
266     }
267 
copyOptionalExtras(Intent inIntent, Intent outIntent)268     private void copyOptionalExtras(Intent inIntent, Intent outIntent) {
269         IntentSender intentSender = inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
270         if (intentSender != null) {
271             outIntent.putExtra(Intent.EXTRA_INTENT, intentSender);
272         }
273         int taskId = inIntent.getIntExtra(Intent.EXTRA_TASK_ID, -1);
274         if (taskId != -1) {
275             outIntent.putExtra(Intent.EXTRA_TASK_ID, taskId);
276         }
277         // If we will launch another activity once credentials are confirmed, exclude from recents.
278         // This is a workaround to a framework bug where affinity is incorrect for activities
279         // that are started from a no display activity, as is ConfirmDeviceCredentialActivity.
280         // TODO: Remove once that bug is fixed.
281         if (intentSender != null || taskId != -1) {
282             outIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
283             outIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
284         }
285     }
286 }
287