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