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