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