• 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 com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
21 
22 import android.app.Activity;
23 import android.app.KeyguardManager;
24 import android.app.admin.DevicePolicyManager;
25 import android.app.trust.TrustManager;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.graphics.Color;
29 import android.hardware.biometrics.BiometricConstants;
30 import android.hardware.biometrics.BiometricPrompt;
31 import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
32 import android.hardware.biometrics.PromptInfo;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.Looper;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.util.Log;
39 import android.view.WindowManager;
40 
41 import androidx.annotation.NonNull;
42 import androidx.fragment.app.FragmentActivity;
43 
44 import com.android.internal.widget.LockPatternUtils;
45 import com.android.settings.R;
46 import com.android.settings.Utils;
47 
48 import java.util.concurrent.Executor;
49 
50 /**
51  * Launch this when you want to confirm the user is present by asking them to enter their
52  * PIN/password/pattern.
53  */
54 public class ConfirmDeviceCredentialActivity extends FragmentActivity {
55     public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
56 
57     /**
58      * If the intent is sent from {@link com.android.systemui.keyguard.WorkLockActivityController}
59      * then check for device policy management flags.
60      */
61     public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY_CONTROLLER =
62             "from_work_lock_activity_controller";
63 
64     // The normal flow that apps go through
65     private static final int CREDENTIAL_NORMAL = 1;
66     // Unlocks the managed profile when the primary profile is unlocked
67     private static final int CREDENTIAL_MANAGED = 2;
68 
69     private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
70 
71     public static class InternalActivity extends ConfirmDeviceCredentialActivity {
72     }
73 
createIntent(CharSequence title, CharSequence details)74     public static Intent createIntent(CharSequence title, CharSequence details) {
75         Intent intent = new Intent();
76         intent.setClassName(SETTINGS_PACKAGE_NAME,
77                 ConfirmDeviceCredentialActivity.class.getName());
78         intent.putExtra(KeyguardManager.EXTRA_TITLE, title);
79         intent.putExtra(KeyguardManager.EXTRA_DESCRIPTION, details);
80         return intent;
81     }
82 
83     private BiometricFragment mBiometricFragment;
84     private DevicePolicyManager mDevicePolicyManager;
85     private LockPatternUtils mLockPatternUtils;
86     private UserManager mUserManager;
87     private TrustManager mTrustManager;
88     private Handler mHandler = new Handler(Looper.getMainLooper());
89     private Context mContext;
90     private boolean mCheckDevicePolicyManager;
91 
92     private String mTitle;
93     private String mDetails;
94     private int mUserId;
95     private int mCredentialMode;
96     private boolean mGoingToBackground;
97     private boolean mWaitingForBiometricCallback;
98 
99     private Executor mExecutor = (runnable -> {
100         mHandler.post(runnable);
101     });
102 
103     private AuthenticationCallback mAuthenticationCallback = new AuthenticationCallback() {
104         @Override
105         public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
106             if (!mGoingToBackground) {
107                 mWaitingForBiometricCallback = false;
108                 if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
109                         || errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
110                     finish();
111                 } else {
112                     // All other errors go to some version of CC
113                     showConfirmCredentials();
114                 }
115             } else if (mWaitingForBiometricCallback) { // mGoingToBackground is true
116                 mWaitingForBiometricCallback = false;
117                 finish();
118             }
119         }
120 
121         @Override
122         public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
123             mWaitingForBiometricCallback = false;
124             mTrustManager.setDeviceLockedForUser(mUserId, false);
125             final boolean isStrongAuth = result.getAuthenticationType()
126                     == BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
127             ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils, mUserManager,
128                     mDevicePolicyManager, mUserId, isStrongAuth);
129             ConfirmDeviceCredentialUtils.checkForPendingIntent(
130                     ConfirmDeviceCredentialActivity.this);
131 
132             setResult(Activity.RESULT_OK);
133             finish();
134         }
135 
136         @Override
137         public void onAuthenticationFailed() {
138             mWaitingForBiometricCallback = false;
139             mDevicePolicyManager.reportFailedBiometricAttempt(mUserId);
140         }
141 
142         @Override
143         public void onSystemEvent(int event) {
144             Log.d(TAG, "SystemEvent: " + event);
145             switch (event) {
146                 case BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL:
147                     finish();
148                     break;
149             }
150         }
151     };
152 
153     @Override
onCreate(Bundle savedInstanceState)154     protected void onCreate(Bundle savedInstanceState) {
155         super.onCreate(savedInstanceState);
156 
157         getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
158         getWindow().setStatusBarColor(Color.TRANSPARENT);
159 
160         mDevicePolicyManager = getSystemService(DevicePolicyManager.class);
161         mUserManager = UserManager.get(this);
162         mTrustManager = getSystemService(TrustManager.class);
163         mLockPatternUtils = new LockPatternUtils(this);
164 
165         Intent intent = getIntent();
166         mContext = this;
167         mCheckDevicePolicyManager = intent
168                 .getBooleanExtra(KeyguardManager.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false);
169         mTitle = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
170         mDetails = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
171         String alternateButton = intent.getStringExtra(
172                 KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
173         boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
174 
175         mUserId = UserHandle.myUserId();
176         if (isInternalActivity()) {
177             try {
178                 mUserId = Utils.getUserIdFromBundle(this, intent.getExtras());
179             } catch (SecurityException se) {
180                 Log.e(TAG, "Invalid intent extra", se);
181             }
182         }
183         final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
184         final boolean isEffectiveUserManagedProfile =
185                 UserManager.get(this).isManagedProfile(effectiveUserId);
186         // if the client app did not hand in a title and we are about to show the work challenge,
187         // check whether there is a policy setting the organization name and use that as title
188         if ((mTitle == null) && isEffectiveUserManagedProfile) {
189             mTitle = getTitleFromOrganizationName(mUserId);
190         }
191 
192         final PromptInfo promptInfo = new PromptInfo();
193         promptInfo.setTitle(mTitle);
194         promptInfo.setDescription(mDetails);
195         promptInfo.setDisallowBiometricsIfPolicyExists(mCheckDevicePolicyManager);
196 
197         final @LockPatternUtils.CredentialType int credentialType = Utils.getCredentialType(
198                 mContext, effectiveUserId);
199         if (mTitle == null) {
200             promptInfo.setDeviceCredentialTitle(
201                     getTitleFromCredentialType(credentialType, isEffectiveUserManagedProfile));
202         }
203         if (mDetails == null) {
204             promptInfo.setSubtitle(
205                     getDetailsFromCredentialType(credentialType, isEffectiveUserManagedProfile));
206         }
207 
208         boolean launchedBiometric = false;
209         boolean launchedCDC = false;
210         // If the target is a managed user and user key not unlocked yet, we will force unlock
211         // tied profile so it will enable work mode and unlock managed profile, when personal
212         // challenge is unlocked.
213         if (frp) {
214             final ChooseLockSettingsHelper.Builder builder =
215                     new ChooseLockSettingsHelper.Builder(this);
216             launchedCDC = builder.setHeader(mTitle) // Show the title in the header location
217                     .setDescription(mDetails)
218                     .setAlternateButton(alternateButton)
219                     .setExternal(true)
220                     .setUserId(LockPatternUtils.USER_FRP)
221                     .show();
222         } else if (isEffectiveUserManagedProfile && isInternalActivity()) {
223             mCredentialMode = CREDENTIAL_MANAGED;
224             if (isBiometricAllowed(effectiveUserId, mUserId)) {
225                 showBiometricPrompt(promptInfo);
226                 launchedBiometric = true;
227             } else {
228                 showConfirmCredentials();
229                 launchedCDC = true;
230             }
231         } else {
232             mCredentialMode = CREDENTIAL_NORMAL;
233             if (isBiometricAllowed(effectiveUserId, mUserId)) {
234                 // Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
235                 // onAuthenticationError and do the right thing automatically.
236                 showBiometricPrompt(promptInfo);
237                 launchedBiometric = true;
238             } else {
239                 showConfirmCredentials();
240                 launchedCDC = true;
241             }
242         }
243 
244         if (launchedCDC) {
245             finish();
246         } else if (launchedBiometric) {
247             // Keep this activity alive until BiometricPrompt goes away
248             mWaitingForBiometricCallback = true;
249         } else {
250             Log.d(TAG, "No pattern, password or PIN set.");
251             setResult(Activity.RESULT_OK);
252             finish();
253         }
254     }
255 
getTitleFromCredentialType(@ockPatternUtils.CredentialType int credentialType, boolean isEffectiveUserManagedProfile)256     private String getTitleFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
257             boolean isEffectiveUserManagedProfile) {
258         switch (credentialType) {
259             case LockPatternUtils.CREDENTIAL_TYPE_PIN:
260                 return isEffectiveUserManagedProfile
261                         ? getString(R.string.lockpassword_confirm_your_work_pin_header)
262                         : getString(R.string.lockpassword_confirm_your_pin_header);
263             case LockPatternUtils.CREDENTIAL_TYPE_PATTERN:
264                 return isEffectiveUserManagedProfile
265                         ? getString(R.string.lockpassword_confirm_your_work_pattern_header)
266                         : getString(R.string.lockpassword_confirm_your_pattern_header);
267             case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD:
268                 return isEffectiveUserManagedProfile
269                         ? getString(R.string.lockpassword_confirm_your_work_password_header)
270                         : getString(R.string.lockpassword_confirm_your_password_header);
271         }
272         return null;
273     }
274 
getDetailsFromCredentialType(@ockPatternUtils.CredentialType int credentialType, boolean isEffectiveUserManagedProfile)275     private String getDetailsFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
276             boolean isEffectiveUserManagedProfile) {
277         switch (credentialType) {
278             case LockPatternUtils.CREDENTIAL_TYPE_PIN:
279                 return isEffectiveUserManagedProfile
280                         ? getString(R.string.lockpassword_confirm_your_pin_generic_profile)
281                         : getString(R.string.lockpassword_confirm_your_pin_generic);
282             case LockPatternUtils.CREDENTIAL_TYPE_PATTERN:
283                 return isEffectiveUserManagedProfile
284                         ? getString(R.string.lockpassword_confirm_your_pattern_generic_profile)
285                         : getString(R.string.lockpassword_confirm_your_pattern_generic);
286             case LockPatternUtils.CREDENTIAL_TYPE_PASSWORD:
287                 return isEffectiveUserManagedProfile
288                         ? getString(R.string.lockpassword_confirm_your_password_generic_profile)
289                         : getString(R.string.lockpassword_confirm_your_password_generic);
290         }
291         return null;
292     }
293 
294     @Override
onStart()295     protected void onStart() {
296         super.onStart();
297         // Translucent activity that is "visible", so it doesn't complain about finish()
298         // not being called before onResume().
299         setVisible(true);
300     }
301 
302     @Override
onPause()303     public void onPause() {
304         super.onPause();
305         if (!isChangingConfigurations()) {
306             mGoingToBackground = true;
307             if (!mWaitingForBiometricCallback) {
308                 finish();
309             }
310         } else {
311             mGoingToBackground = false;
312         }
313     }
314 
315     // User could be locked while Effective user is unlocked even though the effective owns the
316     // credential. Otherwise, biometric can't unlock fbe/keystore through
317     // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
318     // biometric is disabled due to device restart.
isStrongAuthRequired(int effectiveUserId)319     private boolean isStrongAuthRequired(int effectiveUserId) {
320         return !mLockPatternUtils.isBiometricAllowedForUser(effectiveUserId)
321                 || !mUserManager.isUserUnlocked(mUserId);
322     }
323 
isBiometricAllowed(int effectiveUserId, int realUserId)324     private boolean isBiometricAllowed(int effectiveUserId, int realUserId) {
325         return !isStrongAuthRequired(effectiveUserId) && !mLockPatternUtils
326                 .hasPendingEscrowToken(realUserId);
327     }
328 
showBiometricPrompt(PromptInfo promptInfo)329     private void showBiometricPrompt(PromptInfo promptInfo) {
330         mBiometricFragment = (BiometricFragment) getSupportFragmentManager()
331                 .findFragmentByTag(TAG_BIOMETRIC_FRAGMENT);
332         boolean newFragment = false;
333 
334         if (mBiometricFragment == null) {
335             mBiometricFragment = BiometricFragment.newInstance(promptInfo);
336             newFragment = true;
337         }
338         mBiometricFragment.setCallbacks(mExecutor, mAuthenticationCallback);
339         mBiometricFragment.setUser(mUserId);
340 
341         if (newFragment) {
342             getSupportFragmentManager().beginTransaction()
343                     .add(mBiometricFragment, TAG_BIOMETRIC_FRAGMENT).commit();
344         }
345     }
346 
347     /**
348      * Shows ConfirmDeviceCredentials for normal apps.
349      */
showConfirmCredentials()350     private void showConfirmCredentials() {
351         boolean launched = false;
352         // The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
353         // CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
354         // but fake challenge value (0L). This will result in ConfirmLockPassword calling
355         // verifyTiedProfileChallenge() (if it's a profile with unified challenge), due to the
356         // difference between ConfirmLockPassword.startVerifyPassword() and
357         // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here is
358         // necessary when this is part of the turning on work profile flow, because it forces
359         // unlocking the work profile even before the profile is running.
360         // TODO: Remove the duplication of checkPassword and verifyPassword in ConfirmLockPassword,
361         // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to use,
362         // which optionally accepts a challenge.
363         if (mCredentialMode == CREDENTIAL_MANAGED) {
364             final ChooseLockSettingsHelper.Builder builder =
365                     new ChooseLockSettingsHelper.Builder(this);
366             launched = builder.setHeader(mTitle)
367                     .setDescription(mDetails)
368                     .setExternal(true)
369                     .setUserId(mUserId)
370                     .setForceVerifyPath(true)
371                     .show();
372         } else if (mCredentialMode == CREDENTIAL_NORMAL) {
373             final ChooseLockSettingsHelper.Builder builder =
374                     new ChooseLockSettingsHelper.Builder(this);
375             launched = builder.setHeader(mTitle) // Show the title string in the header area
376                     .setDescription(mDetails)
377                     .setExternal(true)
378                     .setUserId(mUserId)
379                     .show();
380         }
381         if (!launched) {
382             Log.d(TAG, "No pin/pattern/pass set");
383             setResult(Activity.RESULT_OK);
384         }
385         finish();
386     }
387 
isInternalActivity()388     private boolean isInternalActivity() {
389         return this instanceof ConfirmDeviceCredentialActivity.InternalActivity;
390     }
391 
getTitleFromOrganizationName(int userId)392     private String getTitleFromOrganizationName(int userId) {
393         DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
394                 Context.DEVICE_POLICY_SERVICE);
395         CharSequence organizationNameForUser = (dpm != null)
396                 ? dpm.getOrganizationNameForUser(userId) : null;
397         return organizationNameForUser != null ? organizationNameForUser.toString() : null;
398     }
399 }
400