• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
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 
18 package com.android.settings.password;
19 
20 import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
21 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
22 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
23 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
24 import static android.content.Intent.EXTRA_PACKAGE_NAME;
25 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
26 
27 import static com.android.systemui.biometrics.Utils.toBitmap;
28 
29 import android.app.Activity;
30 import android.app.KeyguardManager;
31 import android.app.RemoteLockscreenValidationSession;
32 import android.app.admin.DevicePolicyManager;
33 import android.app.admin.ManagedSubscriptionsPolicy;
34 import android.app.trust.TrustManager;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.pm.PackageManager;
39 import android.content.pm.UserProperties;
40 import android.content.res.Configuration;
41 import android.graphics.Bitmap;
42 import android.graphics.Color;
43 import android.hardware.biometrics.BiometricConstants;
44 import android.hardware.biometrics.BiometricManager;
45 import android.hardware.biometrics.BiometricPrompt;
46 import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
47 import android.hardware.biometrics.Flags;
48 import android.hardware.biometrics.PromptInfo;
49 import android.os.Bundle;
50 import android.os.Handler;
51 import android.os.Looper;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.os.storage.StorageManager;
55 import android.text.TextUtils;
56 import android.util.Log;
57 import android.view.WindowManager;
58 
59 import androidx.annotation.NonNull;
60 import androidx.fragment.app.FragmentActivity;
61 
62 import com.android.internal.widget.LockPatternUtils;
63 import com.android.settings.R;
64 import com.android.settings.Utils;
65 
66 import java.util.concurrent.Executor;
67 
68 /**
69  * Launch this when you want to confirm the user is present by asking them to enter their
70  * PIN/password/pattern.
71  */
72 public class ConfirmDeviceCredentialActivity extends FragmentActivity {
73     public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
74 
75     private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
76 
77     /** Use this extra value to provide a custom logo for the biometric prompt. **/
78     public static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY = "custom_logo_res_id";
79     /** Use this extra value to provide a custom logo description for the biometric prompt. **/
80     public static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY =
81             "custom_logo_description";
82     public static final String BIOMETRIC_PROMPT_AUTHENTICATORS = "biometric_prompt_authenticators";
83     public static final String BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT =
84             "biometric_prompt_negative_button_text";
85     public static final String BIOMETRIC_PROMPT_HIDE_BACKGROUND =
86             "biometric_prompt_hide_background";
87     public static final String EXTRA_DATA = "extra_data";
88     public static final int BIOMETRIC_LOCKOUT_ERROR_RESULT = 100;
89 
90     public static class InternalActivity extends ConfirmDeviceCredentialActivity {
91     }
92 
93     private BiometricFragment mBiometricFragment;
94     private DevicePolicyManager mDevicePolicyManager;
95     private LockPatternUtils mLockPatternUtils;
96     private UserManager mUserManager;
97     private TrustManager mTrustManager;
98     private Handler mHandler = new Handler(Looper.getMainLooper());
99     private Context mContext;
100     private boolean mCheckDevicePolicyManager;
101     private boolean mTaskOverlay;
102 
103     private String mTitle;
104     private CharSequence mDetails;
105     private int mUserId;
106     // Used to force the verification path required to unlock profile that shares credentials with
107     // with parent
108     private boolean mForceVerifyPath = false;
109     private boolean mGoingToBackground;
110     private boolean mWaitingForBiometricCallback;
111     private int mBiometricsAuthenticators;
112     private Intent mIntentData;
113 
114     private Executor mExecutor = (runnable -> {
115         mHandler.post(runnable);
116     });
117 
118     private AuthenticationCallback mAuthenticationCallback = new AuthenticationCallback() {
119         @Override
120         public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
121             if (!mGoingToBackground) {
122                 mWaitingForBiometricCallback = false;
123                 if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
124                         || errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
125                     finish();
126                 } else if (mUserManager.getUserInfo(mUserId) == null) {
127                     // This can happen when profile gets wiped due to too many failed auth attempts.
128                     Log.i(TAG, "Finishing, user no longer valid: " + mUserId);
129                     finish();
130                 } else {
131                     if ((mBiometricsAuthenticators
132                             & BiometricManager.Authenticators.DEVICE_CREDENTIAL) != 0) {
133                         // All other errors go to some version of CC
134                         showConfirmCredentials();
135                     } else {
136                         Log.i(TAG, "Finishing, device credential not requested");
137                         if (Flags.mandatoryBiometrics()
138                                 && errorCode == BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT_PERMANENT) {
139                             setResult(BIOMETRIC_LOCKOUT_ERROR_RESULT);
140                         }
141                         finish();
142                     }
143                 }
144             } else if (mWaitingForBiometricCallback) { // mGoingToBackground is true
145                 mWaitingForBiometricCallback = false;
146                 finish();
147             }
148         }
149 
150         @Override
151         public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
152             mWaitingForBiometricCallback = false;
153             mTrustManager.setDeviceLockedForUser(mUserId, false);
154             final boolean isStrongAuth = result.getAuthenticationType()
155                     == BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
156             ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils, mUserManager,
157                     mDevicePolicyManager, mUserId, isStrongAuth);
158             if (isInternalActivity()) {
159                 ConfirmDeviceCredentialUtils.checkForPendingIntent(
160                         ConfirmDeviceCredentialActivity.this);
161             }
162 
163             setResult(Activity.RESULT_OK, mIntentData);
164             finish();
165         }
166 
167         @Override
168         public void onAuthenticationFailed() {
169             mWaitingForBiometricCallback = false;
170             mDevicePolicyManager.reportFailedBiometricAttempt(mUserId);
171         }
172 
173         @Override
174         public void onSystemEvent(int event) {
175             Log.d(TAG, "SystemEvent: " + event);
176             switch (event) {
177                 case BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL:
178                     finish();
179                     break;
180             }
181         }
182     };
183 
184     @Override
onCreate(Bundle savedInstanceState)185     protected void onCreate(Bundle savedInstanceState) {
186         super.onCreate(savedInstanceState);
187 
188         final Intent intent = getIntent();
189         if (intent.getBooleanExtra(BIOMETRIC_PROMPT_HIDE_BACKGROUND, false)) {
190             getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
191             getWindow().setDimAmount(1);
192             intent.removeExtra(BIOMETRIC_PROMPT_HIDE_BACKGROUND);
193         } else {
194             getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
195             getWindow().setStatusBarColor(Color.TRANSPARENT);
196         }
197 
198         mDevicePolicyManager = getSystemService(DevicePolicyManager.class);
199         mUserManager = UserManager.get(this);
200         mTrustManager = getSystemService(TrustManager.class);
201         mLockPatternUtils = new LockPatternUtils(this);
202         mContext = this;
203         mCheckDevicePolicyManager = intent
204                 .getBooleanExtra(KeyguardManager.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
205         mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
206         mDetails = intent.getCharSequenceExtra(KeyguardManager.EXTRA_DESCRIPTION);
207         String alternateButton = intent.getStringExtra(
208                 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
209         mBiometricsAuthenticators = intent.getIntExtra(BIOMETRIC_PROMPT_AUTHENTICATORS,
210                 BiometricManager.Authenticators.DEVICE_CREDENTIAL
211                         | BiometricManager.Authenticators.BIOMETRIC_WEAK);
212         mIntentData = intent.getParcelableExtra(EXTRA_DATA, Intent.class);
213         final String negativeButtonText = intent.getStringExtra(
214                 BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT);
215         final boolean frp =
216                 KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
217         final boolean repairMode =
218                 KeyguardManager.ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL
219                         .equals(intent.getAction());
220         final boolean remoteValidation =
221                 KeyguardManager.ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL.equals(intent.getAction());
222         mTaskOverlay = isInternalActivity()
223                 && intent.getBooleanExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, false);
224         final boolean prepareRepairMode =
225                 KeyguardManager.ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL.equals(
226                         intent.getAction());
227 
228         mUserId = UserHandle.myUserId();
229         if (isInternalActivity()) {
230             try {
231                 mUserId = Utils.getUserIdFromBundle(this, intent.getExtras());
232             } catch (SecurityException se) {
233                 Log.e(TAG, "Invalid intent extra", se);
234             }
235         }
236         final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
237         final boolean isEffectiveUserManagedProfile =
238                 mUserManager.isManagedProfile(effectiveUserId);
239         final UserProperties userProperties =
240                 mUserManager.getUserProperties(UserHandle.of(mUserId));
241         // if the client app did not hand in a title and we are about to show the work challenge,
242         // check whether there is a policy setting the organization name and use that as title
243         if ((mTitle == null) && isEffectiveUserManagedProfile) {
244             mTitle = getTitleFromOrganizationName(mUserId);
245         }
246 
247         final PromptInfo promptInfo = new PromptInfo();
248         promptInfo.setTitle(mTitle);
249         promptInfo.setDescription(mDetails);
250         promptInfo.setDisallowBiometricsIfPolicyExists(mCheckDevicePolicyManager);
251         promptInfo.setAuthenticators(mBiometricsAuthenticators);
252         promptInfo.setNegativeButtonText(negativeButtonText);
253 
254         final String callerPackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
255         if (isInternalActivity() && callerPackageName != null) {
256             promptInfo.setRealCallerForConfirmDeviceCredentialActivity(
257                     new ComponentName(callerPackageName, ""));
258         } else {
259             promptInfo.setRealCallerForConfirmDeviceCredentialActivity(getCallingActivity());
260         }
261 
262         if (android.multiuser.Flags.enablePrivateSpaceFeatures()
263                 && android.multiuser.Flags.usePrivateSpaceIconInBiometricPrompt()
264                 && hasSetBiometricDialogAdvanced(mContext, getLaunchedFromUid())
265         ) {
266             final int iconResId = intent.getIntExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY, 0);
267             if (iconResId != 0) {
268                 final Bitmap iconBitmap = toBitmap(mContext.getDrawable(iconResId));
269                 promptInfo.setLogo(iconResId, iconBitmap);
270             }
271             String logoDescription = intent.getStringExtra(
272                     CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY);
273             if (!TextUtils.isEmpty(logoDescription)) {
274                 promptInfo.setLogoDescription(logoDescription);
275             }
276         }
277 
278         final int policyType = mDevicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType();
279 
280         if (isEffectiveUserManagedProfile
281                 && (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)) {
282             promptInfo.setShowEmergencyCallButton(true);
283         }
284 
285         final @LockPatternUtils.CredentialType int credentialType = Utils.getCredentialType(
286                 mContext, effectiveUserId);
287         if (mTitle == null) {
288             promptInfo.setDeviceCredentialTitle(
289                     getTitleFromCredentialType(credentialType, isEffectiveUserManagedProfile));
290         }
291         if (mDetails == null) {
292             promptInfo.setDeviceCredentialSubtitle(
293                     Utils.getConfirmCredentialStringForUser(this, mUserId, credentialType));
294         }
295 
296         boolean launchedBiometric = false;
297         boolean launchedCDC = false;
298         // If the target is a managed user and user key not unlocked yet, we will force unlock
299         // tied profile so it will enable work mode and unlock managed profile, when personal
300         // challenge is unlocked.
301         if (frp) {
302             final ChooseLockSettingsHelper.Builder builder =
303                     new ChooseLockSettingsHelper.Builder(this);
304             launchedCDC = builder.setHeader(mTitle) // Show the title in the header location
305                     .setDescription(mDetails)
306                     .setAlternateButton(alternateButton)
307                     .setExternal(true)
308                     .setUserId(LockPatternUtils.USER_FRP)
309                     .show();
310         } else if (repairMode) {
311             final ChooseLockSettingsHelper.Builder builder =
312                     new ChooseLockSettingsHelper.Builder(this);
313             launchedCDC = builder.setHeader(mTitle)
314                     .setDescription(mDetails)
315                     .setAlternateButton(alternateButton)
316                     .setExternal(true)
317                     .setUserId(LockPatternUtils.USER_REPAIR_MODE)
318                     .show();
319         } else if (remoteValidation) {
320             RemoteLockscreenValidationSession remoteLockscreenValidationSession =
321                     intent.getParcelableExtra(
322                             KeyguardManager.EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION,
323                             RemoteLockscreenValidationSession.class);
324             ComponentName remoteLockscreenValidationServiceComponent =
325                     intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
326 
327             String checkboxLabel = intent.getStringExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL);
328             final ChooseLockSettingsHelper.Builder builder =
329                     new ChooseLockSettingsHelper.Builder(this);
330             launchedCDC = builder
331                     .setRemoteLockscreenValidation(true)
332                     .setRemoteLockscreenValidationSession(remoteLockscreenValidationSession)
333                     .setRemoteLockscreenValidationServiceComponent(
334                             remoteLockscreenValidationServiceComponent)
335                     .setRequestGatekeeperPasswordHandle(true)
336                     .setReturnCredentials(true) // returns only password handle.
337                     .setHeader(mTitle) // Show the title in the header location
338                     .setDescription(mDetails)
339                     .setCheckboxLabel(checkboxLabel)
340                     .setAlternateButton(alternateButton)
341                     .setExternal(true)
342                     .show();
343             return;
344         } else if (prepareRepairMode) {
345             final ChooseLockSettingsHelper.Builder builder =
346                     new ChooseLockSettingsHelper.Builder(this);
347             launchedCDC = builder.setHeader(mTitle)
348                     .setDescription(mDetails)
349                     .setExternal(true)
350                     .setUserId(mUserId)
351                     .setTaskOverlay(mTaskOverlay)
352                     .setRequestWriteRepairModePassword(true)
353                     .setForceVerifyPath(true)
354                     .show();
355         } else if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mUserId)
356                 && isInternalActivity()) {
357             // When the mForceVerifyPath is set to true, we launch the real confirm credential
358             // activity with an explicit but fake challenge value (0L). This will result in
359             // ConfirmLockPassword calling verifyTiedProfileChallenge() (if it's a profile with
360             // unified challenge), due to the difference between
361             // ConfirmLockPassword.startVerifyPassword() and
362             // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here
363             // is necessary when this is part of the turning on work profile flow, because it forces
364             // unlocking the work profile even before the profile is running.
365             // TODO: Remove the duplication of checkPassword and verifyPassword in
366             //  ConfirmLockPassword,
367             // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to
368             // use, which optionally accepts a challenge.
369             mForceVerifyPath = true;
370             if (isBiometricAllowed(effectiveUserId, mUserId)) {
371                 showBiometricPrompt(promptInfo, mUserId);
372                 launchedBiometric = true;
373             } else {
374                 showConfirmCredentials();
375                 launchedCDC = true;
376             }
377         } else if (android.os.Flags.allowPrivateProfile()
378                 && android.multiuser.Flags.enablePrivateSpaceFeatures()
379                 && userProperties != null
380                 && userProperties.isAuthAlwaysRequiredToDisableQuietMode()
381                 && isInternalActivity()) {
382             // Force verification path is required to be invoked as we might need to verify the
383             // tied profile challenge if the profile is using the unified challenge mode. This
384             // would result in ConfirmLockPassword.startVerifyPassword/
385             // ConfirmLockPattern.startVerifyPattern being called instead of the
386             // startCheckPassword/startCheckPattern
387             mForceVerifyPath = userProperties.isCredentialShareableWithParent();
388             if (android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
389                     && isBiometricAllowed(effectiveUserId, mUserId)) {
390                 setBiometricPromptPropertiesForPrivateProfile(promptInfo);
391                 showBiometricPrompt(promptInfo, effectiveUserId);
392                 launchedBiometric = true;
393             } else if (Flags.privateSpaceBp()) {
394                 promptInfo.setAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL);
395                 setBiometricPromptPropertiesForPrivateProfile(promptInfo);
396                 showBiometricPrompt(promptInfo, mUserId);
397                 launchedBiometric = true;
398             } else {
399                 // TODO(b/376328272): Remove custom private space behavior
400                 mDetails = Utils.getConfirmCredentialStringForUser(this, mUserId, credentialType);
401                 showConfirmCredentials();
402                 launchedCDC = true;
403             }
404         } else {
405             if (isBiometricAllowed(effectiveUserId, mUserId)) {
406                 // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
407                 // onAuthenticationError and do the right thing automatically.
408                 showBiometricPrompt(promptInfo, mUserId);
409                 launchedBiometric = true;
410             } else {
411                 showConfirmCredentials();
412                 launchedCDC = true;
413             }
414         }
415 
416         if (launchedCDC) {
417             finish();
418         } else if (launchedBiometric) {
419             // Keep this activity alive until BiometricPrompt goes away
420             mWaitingForBiometricCallback = true;
421         } else {
422             Log.d(TAG, "No pattern, password or PIN set.");
423             setResult(Activity.RESULT_OK);
424             finish();
425         }
426     }
427 
setBiometricPromptPropertiesForPrivateProfile(PromptInfo promptInfo)428     private static void setBiometricPromptPropertiesForPrivateProfile(PromptInfo promptInfo) {
429         promptInfo.setUseParentProfileForDeviceCredential(true);
430         promptInfo.setConfirmationRequested(false);
431     }
432 
getTitleFromCredentialType(@ockPatternUtils.CredentialType int credentialType, boolean isEffectiveUserManagedProfile)433     private String getTitleFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
434             boolean isEffectiveUserManagedProfile) {
435         switch (credentialType) {
436             case LockPatternUtils.CREDENTIAL_TYPE_PIN:
437                 if (isEffectiveUserManagedProfile) {
438                     return mDevicePolicyManager.getResources().getString(
439                             CONFIRM_WORK_PROFILE_PIN_HEADER,
440                             () -> getString(R.string.lockpassword_confirm_your_work_pin_header));
441                 }
442 
443                 return getString(R.string.lockpassword_confirm_your_pin_header);
444             case LockPatternUtils.CREDENTIAL_TYPE_PATTERN:
445                 if (isEffectiveUserManagedProfile) {
446                     return mDevicePolicyManager.getResources().getString(
447                             CONFIRM_WORK_PROFILE_PATTERN_HEADER,
448                             () -> getString(
449                                     R.string.lockpassword_confirm_your_work_pattern_header));
450                 }
451 
452                 return getString(R.string.lockpassword_confirm_your_pattern_header);
453             case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD:
454                 if (isEffectiveUserManagedProfile) {
455                     return mDevicePolicyManager.getResources().getString(
456                             CONFIRM_WORK_PROFILE_PASSWORD_HEADER,
457                             () -> getString(
458                                     R.string.lockpassword_confirm_your_work_password_header));
459                 }
460 
461                 return getString(R.string.lockpassword_confirm_your_password_header);
462         }
463         return null;
464     }
465 
466     @Override
onStart()467     protected void onStart() {
468         super.onStart();
469         // Translucent activity that is "visible", so it doesn't complain about finish()
470         // not being called before onResume().
471         setVisible(true);
472 
473         if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
474                 != Configuration.UI_MODE_NIGHT_YES) {
475             getWindow().getInsetsController().setSystemBarsAppearance(
476                     APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS);
477         }
478     }
479 
480     @Override
onPause()481     public void onPause() {
482         super.onPause();
483         if (!isChangingConfigurations()) {
484             mGoingToBackground = true;
485             if (!mWaitingForBiometricCallback) {
486                 finish();
487             }
488         } else {
489             mGoingToBackground = false;
490         }
491     }
492 
493     /**
494      * Checks if the calling uid has the permission to set biometric dialog icon and description.
495      */
hasSetBiometricDialogAdvanced(@onNull Context context, int callingUid)496     private static boolean hasSetBiometricDialogAdvanced(@NonNull Context context, int callingUid) {
497         return context.checkPermission(SET_BIOMETRIC_DIALOG_ADVANCED, /* pid */ -1, callingUid)
498                 == PackageManager.PERMISSION_GRANTED;
499     }
500 
501     // User could be locked while Effective user is unlocked even though the effective owns the
502     // credential. Otherwise, biometric can't unlock fbe/keystore through
503     // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
504     // biometric is disabled due to device restart.
isStrongAuthRequired(int effectiveUserId)505     private boolean isStrongAuthRequired(int effectiveUserId) {
506         return !mLockPatternUtils.isBiometricAllowedForUser(effectiveUserId)
507                 || doesUserStateEnforceStrongAuth(mUserId);
508     }
509 
doesUserStateEnforceStrongAuth(int userId)510     private boolean doesUserStateEnforceStrongAuth(int userId) {
511         if (android.os.Flags.allowPrivateProfile()
512                 && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
513                 && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
514             // Check if CE storage for user is locked since biometrics can't unlock fbe/keystore of
515             // the profile user using verifyTiedProfileChallenge. Biometrics can still be used if
516             // the user is stopped with delayed locking (i.e., with storage unlocked), so the user
517             // state (whether the user is in the RUNNING_UNLOCKED state) should not be relied upon.
518             return !StorageManager.isCeStorageUnlocked(userId);
519         }
520         return !mUserManager.isUserUnlocked(userId);
521     }
522 
isBiometricAllowed(int effectiveUserId, int realUserId)523     private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
524         return !isStrongAuthRequired(effectiveUserId) && !mLockPatternUtils
525                 .hasPendingEscrowToken(realUserId);
526     }
527 
showBiometricPrompt(PromptInfo promptInfo, int userId)528     private void showBiometricPrompt(PromptInfo promptInfo, int userId) {
529         mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
530                 .findFragmentByTag(TAG_BIOMETRIC_FRAGMENT);
531         boolean newFragment = false;
532 
533         if (mBiometricFragment == null) {
534             mBiometricFragment = BiometricFragment.newInstance(promptInfo);
535             newFragment = true;
536         }
537         mBiometricFragment.setCallbacks(mExecutor, mAuthenticationCallback);
538         // TODO(b/315864564): Move the logic of choosing the user id against which the
539         //  authentication needs to happen to the BiometricPrompt API
540         mBiometricFragment.setUser(userId);
541 
542         if (newFragment) {
543             getSupportFragmentManager().beginTransaction()
544                     .add(mBiometricFragment, TAG_BIOMETRIC_FRAGMENT).commit();
545         }
546     }
547 
548     /**
549      * Shows ConfirmDeviceCredentials for normal apps.
550      */
showConfirmCredentials()551     private void showConfirmCredentials() {
552         boolean launched = new ChooseLockSettingsHelper.Builder(this)
553                 .setHeader(mTitle)
554                 .setDescription(mDetails)
555                 .setExternal(true)
556                 .setUserId(mUserId)
557                 .setTaskOverlay(mTaskOverlay)
558                 .setForceVerifyPath(mForceVerifyPath)
559                 .show();
560 
561         if (!launched) {
562             Log.d(TAG, "No pin/pattern/pass set");
563             setResult(Activity.RESULT_OK);
564         }
565         finish();
566     }
567 
isInternalActivity()568     private boolean isInternalActivity() {
569         return this instanceof ConfirmDeviceCredentialActivity.InternalActivity;
570     }
571 
getTitleFromOrganizationName(int userId)572     private String getTitleFromOrganizationName(int userId) {
573         DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
574                 Context.DEVICE_POLICY_SERVICE);
575         CharSequence organizationNameForUser = (dpm != null)
576                 ? dpm.getOrganizationNameForUser(userId) : null;
577         return organizationNameForUser != null ? organizationNameForUser.toString() : null;
578     }
579 }
580