blob: 8947f8fa7e2668b43bb62d4a0761fb7b8a750159 [file] [log] [blame]
Jay Civelli09b52282014-07-10 17:27:07 -07001
2/*
3 * Copyright (C) 2014 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Maurice Lam2eb170c2017-04-28 16:18:47 -070018package com.android.settings.password;
Jay Civelli09b52282014-07-10 17:27:07 -070019
Fan Zhangc3fd2892019-01-29 16:00:19 -080020import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
21
Jay Civelli09b52282014-07-10 17:27:07 -070022import android.app.Activity;
Jim Miller75fe9e02014-08-13 14:52:52 -070023import android.app.KeyguardManager;
Benjamin Franz0e111e62016-02-01 17:27:17 +000024import android.app.admin.DevicePolicyManager;
Kevin Chyn127da9c2018-11-09 16:13:53 -080025import android.app.trust.TrustManager;
Benjamin Franz0e111e62016-02-01 17:27:17 +000026import android.content.Context;
Jay Civelli09b52282014-07-10 17:27:07 -070027import android.content.Intent;
Kevin Chyne56df882019-01-24 19:42:05 -080028import android.hardware.biometrics.BiometricConstants;
Kevin Chynb3ee2312018-10-04 15:01:43 -070029import android.hardware.biometrics.BiometricManager;
30import android.hardware.biometrics.BiometricPrompt;
31import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
Jay Civelli09b52282014-07-10 17:27:07 -070032import android.os.Bundle;
Kevin Chynb3ee2312018-10-04 15:01:43 -070033import android.os.Handler;
34import android.os.Looper;
Pavel Grafovf20e3412018-08-09 16:51:55 +010035import android.os.UserHandle;
Clara Bayarric5cde052015-10-22 17:24:43 +010036import android.os.UserManager;
Jay Civelli09b52282014-07-10 17:27:07 -070037import android.util.Log;
38
Kevin Chynb3ee2312018-10-04 15:01:43 -070039import androidx.annotation.NonNull;
40import androidx.fragment.app.FragmentActivity;
41
Ricky Waib4d52ec2016-04-18 15:32:39 +010042import com.android.internal.widget.LockPatternUtils;
Kevin Chynb3ee2312018-10-04 15:01:43 -070043import com.android.settings.R;
Maurice Lam2eb170c2017-04-28 16:18:47 -070044import com.android.settings.Utils;
Ricky Waib4d52ec2016-04-18 15:32:39 +010045
Kevin Chynb3ee2312018-10-04 15:01:43 -070046import java.util.concurrent.Executor;
47
Jay Civelli09b52282014-07-10 17:27:07 -070048/**
49 * Launch this when you want to confirm the user is present by asking them to enter their
50 * PIN/password/pattern.
51 */
Kevin Chynb3ee2312018-10-04 15:01:43 -070052public class ConfirmDeviceCredentialActivity extends FragmentActivity {
Jay Civelli09b52282014-07-10 17:27:07 -070053 public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
54
joshmccloskey9be08992019-10-14 17:45:57 -070055 /**
joshmccloskey53ccc442019-12-18 16:18:31 -080056 * If the intent is sent from {@link com.android.systemui.keyguard.WorkLockActivityController}
57 * then check for device policy management flags.
joshmccloskey9be08992019-10-14 17:45:57 -070058 */
joshmccloskey53ccc442019-12-18 16:18:31 -080059 public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY_CONTROLLER =
60 "from_work_lock_activity_controller";
joshmccloskey9be08992019-10-14 17:45:57 -070061
Kevin Chynb3ee2312018-10-04 15:01:43 -070062 // The normal flow that apps go through
63 private static final int CREDENTIAL_NORMAL = 1;
64 // Unlocks the managed profile when the primary profile is unlocked
65 private static final int CREDENTIAL_MANAGED = 2;
66
67 private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
68
Clara Bayarric5cde052015-10-22 17:24:43 +010069 public static class InternalActivity extends ConfirmDeviceCredentialActivity {
70 }
71
Svetoslav3ea423a2014-10-16 14:44:25 -070072 public static Intent createIntent(CharSequence title, CharSequence details) {
73 Intent intent = new Intent();
Fan Zhangc3fd2892019-01-29 16:00:19 -080074 intent.setClassName(SETTINGS_PACKAGE_NAME,
Svetoslav3ea423a2014-10-16 14:44:25 -070075 ConfirmDeviceCredentialActivity.class.getName());
76 intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
77 intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
78 return intent;
79 }
80
Andres Morales6609b0c2015-04-12 15:38:25 -070081 public static Intent createIntent(CharSequence title, CharSequence details, long challenge) {
82 Intent intent = new Intent();
Fan Zhangc3fd2892019-01-29 16:00:19 -080083 intent.setClassName(SETTINGS_PACKAGE_NAME,
Andres Morales6609b0c2015-04-12 15:38:25 -070084 ConfirmDeviceCredentialActivity.class.getName());
85 intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
86 intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
87 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
88 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
89 return intent;
90 }
91
Kevin Chynb3ee2312018-10-04 15:01:43 -070092 private BiometricManager mBiometricManager;
93 private BiometricFragment mBiometricFragment;
94 private DevicePolicyManager mDevicePolicyManager;
95 private LockPatternUtils mLockPatternUtils;
96 private UserManager mUserManager;
Kevin Chyn127da9c2018-11-09 16:13:53 -080097 private TrustManager mTrustManager;
Kevin Chynb3ee2312018-10-04 15:01:43 -070098 private ChooseLockSettingsHelper mChooseLockSettingsHelper;
99 private Handler mHandler = new Handler(Looper.getMainLooper());
joshmccloskey9be08992019-10-14 17:45:57 -0700100 private Context mContext;
joshmccloskey53ccc442019-12-18 16:18:31 -0800101 private boolean mCheckDevicePolicyManager;
Kevin Chynb3ee2312018-10-04 15:01:43 -0700102
103 private String mTitle;
104 private String mDetails;
105 private int mUserId;
Kevin Chynb3ee2312018-10-04 15:01:43 -0700106 private int mCredentialMode;
107 private boolean mGoingToBackground;
108
109 private Executor mExecutor = (runnable -> {
110 mHandler.post(runnable);
111 });
112
113 private AuthenticationCallback mAuthenticationCallback = new AuthenticationCallback() {
114 public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
115 if (!mGoingToBackground) {
Kevin Chyneeca9182019-03-25 18:15:57 -0700116 if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
117 || errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700118 finish();
119 } else {
120 // All other errors go to some version of CC
121 showConfirmCredentials();
122 }
123 }
Kevin Chynb3ee2312018-10-04 15:01:43 -0700124 }
125
126 public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
Kevin Chyn127da9c2018-11-09 16:13:53 -0800127 mTrustManager.setDeviceLockedForUser(mUserId, false);
128
129 ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils, mUserManager,
130 mUserId);
131 ConfirmDeviceCredentialUtils.checkForPendingIntent(
132 ConfirmDeviceCredentialActivity.this);
133
Kevin Chynb3ee2312018-10-04 15:01:43 -0700134 setResult(Activity.RESULT_OK);
135 finish();
136 }
137 };
138
Kevin Chyneeca9182019-03-25 18:15:57 -0700139 private String getStringForError(int errorCode) {
140 switch (errorCode) {
141 case BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED:
142 return getString(com.android.internal.R.string.biometric_error_user_canceled);
143 case BiometricConstants.BIOMETRIC_ERROR_CANCELED:
144 return getString(com.android.internal.R.string.biometric_error_canceled);
145 default:
146 return null;
147 }
148 }
149
Jay Civelli09b52282014-07-10 17:27:07 -0700150 @Override
Kevin Chynb3ee2312018-10-04 15:01:43 -0700151 protected void onCreate(Bundle savedInstanceState) {
Jay Civelli09b52282014-07-10 17:27:07 -0700152 super.onCreate(savedInstanceState);
153
Kevin Chynb3ee2312018-10-04 15:01:43 -0700154 mBiometricManager = getSystemService(BiometricManager.class);
155 mDevicePolicyManager = getSystemService(DevicePolicyManager.class);
156 mUserManager = UserManager.get(this);
Kevin Chyn127da9c2018-11-09 16:13:53 -0800157 mTrustManager = getSystemService(TrustManager.class);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700158 mLockPatternUtils = new LockPatternUtils(this);
159
Jay Civelli09b52282014-07-10 17:27:07 -0700160 Intent intent = getIntent();
joshmccloskey9be08992019-10-14 17:45:57 -0700161 mContext = this;
joshmccloskey53ccc442019-12-18 16:18:31 -0800162 mCheckDevicePolicyManager = intent
163 .getBooleanExtra(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700164 mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
165 mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
Adrian Roos5a9a3cd2017-03-30 18:02:25 -0700166 String alternateButton = intent.getStringExtra(
167 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
168 boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
169
Kevin Chynb3ee2312018-10-04 15:01:43 -0700170 mUserId = UserHandle.myUserId();
Clara Bayarric5cde052015-10-22 17:24:43 +0100171 if (isInternalActivity()) {
Benjamin Franz194300d2016-01-13 12:16:25 +0000172 try {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700173 mUserId = Utils.getUserIdFromBundle(this, intent.getExtras());
Benjamin Franz194300d2016-01-13 12:16:25 +0000174 } catch (SecurityException se) {
175 Log.e(TAG, "Invalid intent extra", se);
Clara Bayarric5cde052015-10-22 17:24:43 +0100176 }
177 }
Kevin Chyne264bf32019-02-05 18:55:52 -0800178 final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700179 final boolean isManagedProfile = UserManager.get(this).isManagedProfile(mUserId);
Benjamin Franz0e111e62016-02-01 17:27:17 +0000180 // if the client app did not hand in a title and we are about to show the work challenge,
181 // check whether there is a policy setting the organization name and use that as title
Kevin Chynb3ee2312018-10-04 15:01:43 -0700182 if ((mTitle == null) && isManagedProfile) {
183 mTitle = getTitleFromOrganizationName(mUserId);
Benjamin Franz0e111e62016-02-01 17:27:17 +0000184 }
Kevin Chynb3ee2312018-10-04 15:01:43 -0700185 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
Ricky Waib4d52ec2016-04-18 15:32:39 +0100186 final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700187
joshmccloskey37860162019-09-17 14:05:51 -0700188 final Bundle bpBundle = new Bundle();
joshmccloskey37860162019-09-17 14:05:51 -0700189 bpBundle.putString(BiometricPrompt.KEY_TITLE, mTitle);
190 bpBundle.putString(BiometricPrompt.KEY_DESCRIPTION, mDetails);
joshmccloskey53ccc442019-12-18 16:18:31 -0800191 bpBundle.putBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS,
192 mCheckDevicePolicyManager);
Kevin Chyne56df882019-01-24 19:42:05 -0800193
Kevin Chynb3ee2312018-10-04 15:01:43 -0700194 boolean launchedBiometric = false;
195 boolean launchedCDC = false;
Ricky Waib4d52ec2016-04-18 15:32:39 +0100196 // If the target is a managed user and user key not unlocked yet, we will force unlock
197 // tied profile so it will enable work mode and unlock managed profile, when personal
198 // challenge is unlocked.
Adrian Roos5a9a3cd2017-03-30 18:02:25 -0700199 if (frp) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700200 launchedCDC = mChooseLockSettingsHelper.launchFrpConfirmationActivity(
201 0, mTitle, mDetails, alternateButton);
Adrian Roos5a9a3cd2017-03-30 18:02:25 -0700202 } else if (isManagedProfile && isInternalActivity()
Kevin Chynb3ee2312018-10-04 15:01:43 -0700203 && !lockPatternUtils.isSeparateProfileChallengeEnabled(mUserId)) {
204 mCredentialMode = CREDENTIAL_MANAGED;
joshmccloskey53ccc442019-12-18 16:18:31 -0800205 if (isBiometricAllowed(effectiveUserId, mUserId)) {
Kevin Chyne56df882019-01-24 19:42:05 -0800206 showBiometricPrompt(bpBundle);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700207 launchedBiometric = true;
208 } else {
209 showConfirmCredentials();
Kevin Chyn127da9c2018-11-09 16:13:53 -0800210 launchedCDC = true;
Kevin Chynb3ee2312018-10-04 15:01:43 -0700211 }
212 } else {
213 mCredentialMode = CREDENTIAL_NORMAL;
Rubin Xue0efe272019-03-18 14:58:53 +0000214 if (isBiometricAllowed(effectiveUserId, mUserId)) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700215 // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
216 // onAuthenticationError and do the right thing automatically.
Kevin Chyne56df882019-01-24 19:42:05 -0800217 showBiometricPrompt(bpBundle);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700218 launchedBiometric = true;
219 } else {
220 showConfirmCredentials();
Kevin Chyn127da9c2018-11-09 16:13:53 -0800221 launchedCDC = true;
Kevin Chynb3ee2312018-10-04 15:01:43 -0700222 }
223 }
224
225 if (launchedCDC) {
226 finish();
227 } else if (launchedBiometric) {
228 // Keep this activity alive until BiometricPrompt goes away
229 } else {
230 Log.d(TAG, "No pattern, password or PIN set.");
231 setResult(Activity.RESULT_OK);
232 finish();
233 }
234 }
235
236 @Override
237 protected void onStart() {
238 super.onStart();
239 // Translucent activity that is "visible", so it doesn't complain about finish()
240 // not being called before onResume().
241 setVisible(true);
242 }
243
244 @Override
245 public void onPause() {
246 super.onPause();
247 if (!isChangingConfigurations()) {
248 mGoingToBackground = true;
Kevin Chynb3ee2312018-10-04 15:01:43 -0700249 finish();
250 } else {
251 mGoingToBackground = false;
252 }
253 }
254
255 // User could be locked while Effective user is unlocked even though the effective owns the
256 // credential. Otherwise, biometric can't unlock fbe/keystore through
257 // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
258 // biometric is disabled due to device restart.
Kevin Chyn127da9c2018-11-09 16:13:53 -0800259 private boolean isStrongAuthRequired(int effectiveUserId) {
260 return !mLockPatternUtils.isBiometricAllowedForUser(effectiveUserId)
Kevin Chynb3ee2312018-10-04 15:01:43 -0700261 || !mUserManager.isUserUnlocked(mUserId);
262 }
263
Rubin Xue0efe272019-03-18 14:58:53 +0000264 private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
joshmccloskey53ccc442019-12-18 16:18:31 -0800265 return !isStrongAuthRequired(effectiveUserId) && !mLockPatternUtils
266 .hasPendingEscrowToken(realUserId);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700267 }
268
Kevin Chyne56df882019-01-24 19:42:05 -0800269 private void showBiometricPrompt(Bundle bundle) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700270 mBiometricManager.setActiveUser(mUserId);
271
272 mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
273 .findFragmentByTag(TAG_BIOMETRIC_FRAGMENT);
274 boolean newFragment = false;
275
276 if (mBiometricFragment == null) {
Kevin Chyne56df882019-01-24 19:42:05 -0800277 mBiometricFragment = BiometricFragment.newInstance(bundle);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700278 newFragment = true;
279 }
280 mBiometricFragment.setCallbacks(mExecutor, mAuthenticationCallback);
Kevin Chyn127da9c2018-11-09 16:13:53 -0800281 mBiometricFragment.setUser(mUserId);
Kevin Chynb3ee2312018-10-04 15:01:43 -0700282
283 if (newFragment) {
284 getSupportFragmentManager().beginTransaction()
285 .add(mBiometricFragment, TAG_BIOMETRIC_FRAGMENT).commit();
286 }
287 }
288
289 /**
290 * Shows ConfirmDeviceCredentials for normal apps.
291 */
292 private void showConfirmCredentials() {
293 boolean launched = false;
Rubin Xue0efe272019-03-18 14:58:53 +0000294 // The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
295 // CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
296 // but dummy challenge value (0L). This will result in ConfirmLockPassword calling
297 // verifyTiedProfileChallenge() (if it's a profile with unified challenge), due to the
298 // difference between ConfirmLockPassword.startVerifyPassword() and
299 // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here is
300 // necessary when this is part of the turning on work profile flow, because it forces
301 // unlocking the work profile even before the profile is running.
302 // TODO: Remove the duplication of checkPassword and verifyPassword in ConfirmLockPassword,
303 // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to use,
304 // which optionally accepts a challenge.
Kevin Chynb3ee2312018-10-04 15:01:43 -0700305 if (mCredentialMode == CREDENTIAL_MANAGED) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700306 launched = mChooseLockSettingsHelper
307 .launchConfirmationActivityWithExternalAndChallenge(
308 0 /* request code */, null /* title */, mTitle, mDetails,
309 true /* isExternal */, 0L /* challenge */, mUserId);
joshmccloskey53ccc442019-12-18 16:18:31 -0800310 } else if (mCredentialMode == CREDENTIAL_NORMAL) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700311 launched = mChooseLockSettingsHelper.launchConfirmationActivity(
312 0 /* request code */, null /* title */,
313 mTitle, mDetails, false /* returnCredentials */, true /* isExternal */,
314 mUserId);
Ricky Waib4d52ec2016-04-18 15:32:39 +0100315 }
316 if (!launched) {
Kevin Chynb3ee2312018-10-04 15:01:43 -0700317 Log.d(TAG, "No pin/pattern/pass set");
Jorim Jaggi74a22832015-09-10 20:12:19 -0700318 setResult(Activity.RESULT_OK);
Jay Civelli09b52282014-07-10 17:27:07 -0700319 }
Jay Civelli09b52282014-07-10 17:27:07 -0700320 finish();
321 }
Clara Bayarric5cde052015-10-22 17:24:43 +0100322
Kevin Chynb3ee2312018-10-04 15:01:43 -0700323 @Override
324 public void finish() {
325 super.finish();
326 // Finish without animation since the activity is just there so we can launch
327 // BiometricPrompt.
328 overridePendingTransition(R.anim.confirm_credential_biometric_transition_enter, 0);
329 }
330
Clara Bayarric5cde052015-10-22 17:24:43 +0100331 private boolean isInternalActivity() {
332 return this instanceof ConfirmDeviceCredentialActivity.InternalActivity;
333 }
Benjamin Franz0e111e62016-02-01 17:27:17 +0000334
335 private String getTitleFromOrganizationName(int userId) {
336 DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
337 Context.DEVICE_POLICY_SERVICE);
Michal Karpinski435ec6e2016-04-12 15:20:08 +0100338 CharSequence organizationNameForUser = (dpm != null)
339 ? dpm.getOrganizationNameForUser(userId) : null;
340 return organizationNameForUser != null ? organizationNameForUser.toString() : null;
Benjamin Franz0e111e62016-02-01 17:27:17 +0000341 }
Jay Civelli09b52282014-07-10 17:27:07 -0700342}