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