1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar; 18 19 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; 20 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE; 21 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE; 22 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; 23 import static android.hardware.biometrics.BiometricSourceType.FACE; 24 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; 25 import static android.view.View.GONE; 26 import static android.view.View.VISIBLE; 27 28 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; 29 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; 30 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; 31 import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser; 32 import static com.android.systemui.DejankUtils.whitelistIpcs; 33 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION; 34 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; 35 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; 36 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; 37 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP; 38 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; 39 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; 40 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; 41 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE; 42 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; 43 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; 44 import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; 45 import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; 46 import static com.android.systemui.plugins.log.LogLevel.ERROR; 47 48 import android.app.AlarmManager; 49 import android.app.admin.DevicePolicyManager; 50 import android.content.BroadcastReceiver; 51 import android.content.Context; 52 import android.content.Intent; 53 import android.content.IntentFilter; 54 import android.content.pm.UserInfo; 55 import android.content.res.ColorStateList; 56 import android.content.res.Resources; 57 import android.graphics.Color; 58 import android.hardware.biometrics.BiometricSourceType; 59 import android.hardware.face.FaceManager; 60 import android.hardware.fingerprint.FingerprintManager; 61 import android.os.BatteryManager; 62 import android.os.Handler; 63 import android.os.Looper; 64 import android.os.Message; 65 import android.os.RemoteException; 66 import android.os.UserHandle; 67 import android.os.UserManager; 68 import android.text.TextUtils; 69 import android.text.format.Formatter; 70 import android.view.View; 71 import android.view.ViewGroup; 72 import android.view.accessibility.AccessibilityManager; 73 74 import androidx.annotation.NonNull; 75 import androidx.annotation.Nullable; 76 77 import com.android.internal.annotations.VisibleForTesting; 78 import com.android.internal.app.IBatteryStats; 79 import com.android.internal.widget.LockPatternUtils; 80 import com.android.keyguard.KeyguardUpdateMonitor; 81 import com.android.keyguard.KeyguardUpdateMonitorCallback; 82 import com.android.keyguard.TrustGrantFlags; 83 import com.android.keyguard.logging.KeyguardLogger; 84 import com.android.settingslib.Utils; 85 import com.android.settingslib.fuelgauge.BatteryStatus; 86 import com.android.systemui.R; 87 import com.android.systemui.biometrics.AuthController; 88 import com.android.systemui.biometrics.FaceHelpMessageDeferral; 89 import com.android.systemui.broadcast.BroadcastDispatcher; 90 import com.android.systemui.dagger.SysUISingleton; 91 import com.android.systemui.dagger.qualifiers.Background; 92 import com.android.systemui.dagger.qualifiers.Main; 93 import com.android.systemui.dock.DockManager; 94 import com.android.systemui.keyguard.KeyguardIndication; 95 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; 96 import com.android.systemui.keyguard.ScreenLifecycle; 97 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; 98 import com.android.systemui.plugins.FalsingManager; 99 import com.android.systemui.plugins.log.LogLevel; 100 import com.android.systemui.plugins.statusbar.StatusBarStateController; 101 import com.android.systemui.statusbar.phone.KeyguardBypassController; 102 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; 103 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 104 import com.android.systemui.statusbar.policy.KeyguardStateController; 105 import com.android.systemui.util.AlarmTimeout; 106 import com.android.systemui.util.concurrency.DelayableExecutor; 107 import com.android.systemui.util.wakelock.SettableWakeLock; 108 import com.android.systemui.util.wakelock.WakeLock; 109 110 import java.io.PrintWriter; 111 import java.text.NumberFormat; 112 import java.util.HashSet; 113 import java.util.Set; 114 115 import javax.inject.Inject; 116 117 /** 118 * Controls the indications and error messages shown on the Keyguard 119 * 120 * On AoD, only one message shows with the following priorities: 121 * 1. Biometric 122 * 2. Transient 123 * 3. Charging alignment 124 * 4. Battery information 125 * 126 * On the lock screen, message rotate through different message types. 127 * See {@link KeyguardIndicationRotateTextViewController.IndicationType} for the list of types. 128 */ 129 @SysUISingleton 130 public class KeyguardIndicationController { 131 132 public static final String TAG = "KeyguardIndication"; 133 private static final boolean DEBUG_CHARGING_SPEED = false; 134 135 private static final int MSG_SHOW_ACTION_TO_UNLOCK = 1; 136 private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 2; 137 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; 138 public static final long DEFAULT_HIDE_DELAY_MS = 139 3500 + KeyguardIndicationTextView.Y_IN_DURATION; 140 141 private final Context mContext; 142 private final BroadcastDispatcher mBroadcastDispatcher; 143 private final KeyguardStateController mKeyguardStateController; 144 protected final StatusBarStateController mStatusBarStateController; 145 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 146 private final AuthController mAuthController; 147 private final KeyguardLogger mKeyguardLogger; 148 private ViewGroup mIndicationArea; 149 private KeyguardIndicationTextView mTopIndicationView; 150 private KeyguardIndicationTextView mLockScreenIndicationView; 151 private final IBatteryStats mBatteryInfo; 152 private final SettableWakeLock mWakeLock; 153 private final DockManager mDockManager; 154 private final DevicePolicyManager mDevicePolicyManager; 155 private final UserManager mUserManager; 156 protected final @Main DelayableExecutor mExecutor; 157 protected final @Background DelayableExecutor mBackgroundExecutor; 158 private final LockPatternUtils mLockPatternUtils; 159 private final FalsingManager mFalsingManager; 160 private final KeyguardBypassController mKeyguardBypassController; 161 private final AccessibilityManager mAccessibilityManager; 162 private final Handler mHandler; 163 private final AlternateBouncerInteractor mAlternateBouncerInteractor; 164 165 @VisibleForTesting 166 public KeyguardIndicationRotateTextViewController mRotateTextViewController; 167 private BroadcastReceiver mBroadcastReceiver; 168 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 169 170 private String mPersistentUnlockMessage; 171 private String mAlignmentIndication; 172 private CharSequence mTrustGrantedIndication; 173 private CharSequence mTransientIndication; 174 private CharSequence mBiometricMessage; 175 private CharSequence mBiometricMessageFollowUp; 176 protected ColorStateList mInitialTextColorState; 177 private boolean mVisible; 178 private boolean mOrganizationOwnedDevice; 179 180 // these all assume the device is plugged in (wired/wireless/docked) AND chargingOrFull: 181 private boolean mPowerPluggedIn; 182 private boolean mPowerPluggedInWired; 183 private boolean mPowerPluggedInWireless; 184 private boolean mPowerPluggedInDock; 185 186 private boolean mPowerCharged; 187 private boolean mBatteryOverheated; 188 private boolean mEnableBatteryDefender; 189 private int mChargingSpeed; 190 private int mChargingWattage; 191 private int mBatteryLevel; 192 private boolean mBatteryPresent = true; 193 private long mChargingTimeRemaining; 194 private String mBiometricErrorMessageToShowOnScreenOn; 195 private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow; 196 private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral; 197 private boolean mInited; 198 199 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; 200 201 private boolean mDozing; 202 private final ScreenLifecycle mScreenLifecycle; 203 private final ScreenLifecycle.Observer mScreenObserver = 204 new ScreenLifecycle.Observer() { 205 @Override 206 public void onScreenTurnedOn() { 207 mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON); 208 if (mBiometricErrorMessageToShowOnScreenOn != null) { 209 String followUpMessage = mFaceLockedOutThisAuthSession 210 ? faceLockedOutFollowupMessage() : null; 211 showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn, followUpMessage); 212 // We want to keep this message around in case the screen was off 213 hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS); 214 mBiometricErrorMessageToShowOnScreenOn = null; 215 } 216 } 217 }; 218 private boolean mFaceLockedOutThisAuthSession; 219 220 // Use AlarmTimeouts to guarantee that the events are handled even if scheduled and 221 // triggered while the device is asleep 222 private final AlarmTimeout mHideTransientMessageHandler; 223 private final AlarmTimeout mHideBiometricMessageHandler; 224 225 /** 226 * Creates a new KeyguardIndicationController and registers callbacks. 227 */ 228 @Inject KeyguardIndicationController( Context context, @Main Looper mainLooper, WakeLock.Builder wakeLockBuilder, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, BroadcastDispatcher broadcastDispatcher, DevicePolicyManager devicePolicyManager, IBatteryStats iBatteryStats, UserManager userManager, @Main DelayableExecutor executor, @Background DelayableExecutor bgExecutor, FalsingManager falsingManager, AuthController authController, LockPatternUtils lockPatternUtils, ScreenLifecycle screenLifecycle, KeyguardBypassController keyguardBypassController, AccessibilityManager accessibilityManager, FaceHelpMessageDeferral faceHelpMessageDeferral, KeyguardLogger keyguardLogger, AlternateBouncerInteractor alternateBouncerInteractor, AlarmManager alarmManager )229 public KeyguardIndicationController( 230 Context context, 231 @Main Looper mainLooper, 232 WakeLock.Builder wakeLockBuilder, 233 KeyguardStateController keyguardStateController, 234 StatusBarStateController statusBarStateController, 235 KeyguardUpdateMonitor keyguardUpdateMonitor, 236 DockManager dockManager, 237 BroadcastDispatcher broadcastDispatcher, 238 DevicePolicyManager devicePolicyManager, 239 IBatteryStats iBatteryStats, 240 UserManager userManager, 241 @Main DelayableExecutor executor, 242 @Background DelayableExecutor bgExecutor, 243 FalsingManager falsingManager, 244 AuthController authController, 245 LockPatternUtils lockPatternUtils, 246 ScreenLifecycle screenLifecycle, 247 KeyguardBypassController keyguardBypassController, 248 AccessibilityManager accessibilityManager, 249 FaceHelpMessageDeferral faceHelpMessageDeferral, 250 KeyguardLogger keyguardLogger, 251 AlternateBouncerInteractor alternateBouncerInteractor, 252 AlarmManager alarmManager 253 ) { 254 mContext = context; 255 mBroadcastDispatcher = broadcastDispatcher; 256 mDevicePolicyManager = devicePolicyManager; 257 mKeyguardStateController = keyguardStateController; 258 mStatusBarStateController = statusBarStateController; 259 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 260 mDockManager = dockManager; 261 mWakeLock = new SettableWakeLock( 262 wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG); 263 mBatteryInfo = iBatteryStats; 264 mUserManager = userManager; 265 mExecutor = executor; 266 mBackgroundExecutor = bgExecutor; 267 mLockPatternUtils = lockPatternUtils; 268 mAuthController = authController; 269 mFalsingManager = falsingManager; 270 mKeyguardBypassController = keyguardBypassController; 271 mAccessibilityManager = accessibilityManager; 272 mScreenLifecycle = screenLifecycle; 273 mKeyguardLogger = keyguardLogger; 274 mScreenLifecycle.addObserver(mScreenObserver); 275 mAlternateBouncerInteractor = alternateBouncerInteractor; 276 277 mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; 278 mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); 279 int[] msgIds = context.getResources().getIntArray( 280 com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled); 281 for (int msgId : msgIds) { 282 mCoExFaceAcquisitionMsgIdsToShow.add(msgId); 283 } 284 285 mHandler = new Handler(mainLooper) { 286 @Override 287 public void handleMessage(Message msg) { 288 if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) { 289 showActionToUnlock(); 290 } else if (msg.what == MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON) { 291 mBiometricErrorMessageToShowOnScreenOn = null; 292 } 293 } 294 }; 295 296 mHideTransientMessageHandler = new AlarmTimeout( 297 alarmManager, 298 this::hideTransientIndication, 299 TAG, 300 mHandler 301 ); 302 mHideBiometricMessageHandler = new AlarmTimeout( 303 alarmManager, 304 this::hideBiometricMessage, 305 TAG, 306 mHandler 307 ); 308 } 309 310 /** Call this after construction to finish setting up the instance. */ init()311 public void init() { 312 if (mInited) { 313 return; 314 } 315 mInited = true; 316 317 mDockManager.addAlignmentStateListener( 318 alignState -> mHandler.post(() -> handleAlignStateChanged(alignState))); 319 mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); 320 mStatusBarStateController.addCallback(mStatusBarStateListener); 321 mKeyguardStateController.addCallback(mKeyguardStateCallback); 322 323 mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing()); 324 } 325 setIndicationArea(ViewGroup indicationArea)326 public void setIndicationArea(ViewGroup indicationArea) { 327 mIndicationArea = indicationArea; 328 mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text); 329 mLockScreenIndicationView = indicationArea.findViewById( 330 R.id.keyguard_indication_text_bottom); 331 mInitialTextColorState = mTopIndicationView != null 332 ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE); 333 mRotateTextViewController = new KeyguardIndicationRotateTextViewController( 334 mLockScreenIndicationView, 335 mExecutor, 336 mStatusBarStateController, 337 mKeyguardLogger 338 ); 339 updateDeviceEntryIndication(false /* animate */); 340 updateOrganizedOwnedDevice(); 341 if (mBroadcastReceiver == null) { 342 // Update the disclosure proactively to avoid IPC on the critical path. 343 mBroadcastReceiver = new BroadcastReceiver() { 344 @Override 345 public void onReceive(Context context, Intent intent) { 346 updateOrganizedOwnedDevice(); 347 } 348 }; 349 IntentFilter intentFilter = new IntentFilter(); 350 intentFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 351 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 352 mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter); 353 } 354 } 355 356 /** 357 * Cleanup 358 */ destroy()359 public void destroy() { 360 mHandler.removeCallbacksAndMessages(null); 361 mHideBiometricMessageHandler.cancel(); 362 mHideTransientMessageHandler.cancel(); 363 mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); 364 } 365 handleAlignStateChanged(int alignState)366 private void handleAlignStateChanged(int alignState) { 367 String alignmentIndication = ""; 368 if (alignState == DockManager.ALIGN_STATE_POOR) { 369 alignmentIndication = 370 mContext.getResources().getString(R.string.dock_alignment_slow_charging); 371 } else if (alignState == DockManager.ALIGN_STATE_TERRIBLE) { 372 alignmentIndication = 373 mContext.getResources().getString(R.string.dock_alignment_not_charging); 374 } 375 if (!alignmentIndication.equals(mAlignmentIndication)) { 376 mAlignmentIndication = alignmentIndication; 377 updateDeviceEntryIndication(false); 378 } 379 } 380 381 /** 382 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this 383 * {@link KeyguardIndicationController}. 384 * 385 * <p>Subclasses may override this method to extend or change the callback behavior by extending 386 * the {@link BaseKeyguardCallback}. 387 * 388 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the 389 * same instance. 390 */ getKeyguardCallback()391 protected KeyguardUpdateMonitorCallback getKeyguardCallback() { 392 if (mUpdateMonitorCallback == null) { 393 mUpdateMonitorCallback = new BaseKeyguardCallback(); 394 } 395 return mUpdateMonitorCallback; 396 } 397 updateLockScreenIndications(boolean animate, int userId)398 private void updateLockScreenIndications(boolean animate, int userId) { 399 // update transient messages: 400 updateBiometricMessage(); 401 updateTransient(); 402 403 // Update persistent messages. The following methods should only be called if we're on the 404 // lock screen: 405 updateLockScreenDisclosureMsg(); 406 updateLockScreenOwnerInfo(); 407 updateLockScreenBatteryMsg(animate); 408 updateLockScreenUserLockedMsg(userId); 409 updateLockScreenTrustMsg(userId, getTrustGrantedIndication(), getTrustManagedIndication()); 410 updateLockScreenAlignmentMsg(); 411 updateLockScreenLogoutView(); 412 updateLockScreenPersistentUnlockMsg(); 413 } 414 updateOrganizedOwnedDevice()415 private void updateOrganizedOwnedDevice() { 416 // avoid calling this method since it has an IPC 417 mOrganizationOwnedDevice = whitelistIpcs(this::isOrganizationOwnedDevice); 418 updateDeviceEntryIndication(false); 419 } 420 updateLockScreenDisclosureMsg()421 private void updateLockScreenDisclosureMsg() { 422 if (mOrganizationOwnedDevice) { 423 mBackgroundExecutor.execute(() -> { 424 final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); 425 final CharSequence disclosure = getDisclosureText(organizationName); 426 427 mExecutor.execute(() -> { 428 if (mKeyguardStateController.isShowing()) { 429 mRotateTextViewController.updateIndication( 430 INDICATION_TYPE_DISCLOSURE, 431 new KeyguardIndication.Builder() 432 .setMessage(disclosure) 433 .setTextColor(mInitialTextColorState) 434 .build(), 435 /* updateImmediately */ false); 436 } 437 }); 438 }); 439 } else { 440 mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE); 441 } 442 } 443 getDisclosureText(@ullable CharSequence organizationName)444 private CharSequence getDisclosureText(@Nullable CharSequence organizationName) { 445 final Resources packageResources = mContext.getResources(); 446 if (organizationName == null) { 447 return mDevicePolicyManager.getResources().getString( 448 KEYGUARD_MANAGEMENT_DISCLOSURE, 449 () -> packageResources.getString(R.string.do_disclosure_generic)); 450 } else if (mDevicePolicyManager.isDeviceManaged() 451 && mDevicePolicyManager.getDeviceOwnerType( 452 mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) 453 == DEVICE_OWNER_TYPE_FINANCED) { 454 return packageResources.getString(R.string.do_financed_disclosure_with_name, 455 organizationName); 456 } else { 457 return mDevicePolicyManager.getResources().getString( 458 KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE, 459 () -> packageResources.getString( 460 R.string.do_disclosure_with_name, organizationName), 461 organizationName); 462 } 463 } 464 updateLockScreenOwnerInfo()465 private void updateLockScreenOwnerInfo() { 466 // Check device owner info on a bg thread. 467 // It makes multiple IPCs that could block the thread it's run on. 468 mBackgroundExecutor.execute(() -> { 469 String info = mLockPatternUtils.getDeviceOwnerInfo(); 470 if (info == null) { 471 // Use the current user owner information if enabled. 472 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( 473 getCurrentUser()); 474 if (ownerInfoEnabled) { 475 info = mLockPatternUtils.getOwnerInfo(getCurrentUser()); 476 } 477 } 478 479 // Update the UI on the main thread. 480 final String finalInfo = info; 481 mExecutor.execute(() -> { 482 if (!TextUtils.isEmpty(finalInfo) && mKeyguardStateController.isShowing()) { 483 mRotateTextViewController.updateIndication( 484 INDICATION_TYPE_OWNER_INFO, 485 new KeyguardIndication.Builder() 486 .setMessage(finalInfo) 487 .setTextColor(mInitialTextColorState) 488 .build(), 489 false); 490 } else { 491 mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO); 492 } 493 }); 494 }); 495 } 496 updateLockScreenBatteryMsg(boolean animate)497 private void updateLockScreenBatteryMsg(boolean animate) { 498 if (mPowerPluggedIn || mEnableBatteryDefender) { 499 String powerIndication = computePowerIndication(); 500 if (DEBUG_CHARGING_SPEED) { 501 powerIndication += ", " + (mChargingWattage / 1000) + " mW"; 502 } 503 504 mKeyguardLogger.logUpdateBatteryIndication(powerIndication, mPowerPluggedIn); 505 mRotateTextViewController.updateIndication( 506 INDICATION_TYPE_BATTERY, 507 new KeyguardIndication.Builder() 508 .setMessage(powerIndication) 509 .setTextColor(mInitialTextColorState) 510 .build(), 511 animate); 512 } else { 513 mKeyguardLogger.log(TAG, LogLevel.DEBUG, "hide battery indication"); 514 // don't show the charging information if device isn't plugged in 515 mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY); 516 } 517 } 518 updateLockScreenUserLockedMsg(int userId)519 private void updateLockScreenUserLockedMsg(int userId) { 520 if (!mKeyguardUpdateMonitor.isUserUnlocked(userId) 521 || mKeyguardUpdateMonitor.isEncryptedOrLockdown(userId)) { 522 mRotateTextViewController.updateIndication( 523 INDICATION_TYPE_USER_LOCKED, 524 new KeyguardIndication.Builder() 525 .setMessage(mContext.getResources().getText( 526 com.android.internal.R.string.lockscreen_storage_locked)) 527 .setTextColor(mInitialTextColorState) 528 .build(), 529 false); 530 } else { 531 mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED); 532 } 533 } 534 updateBiometricMessage()535 private void updateBiometricMessage() { 536 if (mDozing) { 537 updateDeviceEntryIndication(false); 538 return; 539 } 540 541 if (!TextUtils.isEmpty(mBiometricMessage)) { 542 mRotateTextViewController.updateIndication( 543 INDICATION_TYPE_BIOMETRIC_MESSAGE, 544 new KeyguardIndication.Builder() 545 .setMessage(mBiometricMessage) 546 .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION) 547 .setTextColor(mInitialTextColorState) 548 .build(), 549 true 550 ); 551 } else { 552 mRotateTextViewController.hideIndication( 553 INDICATION_TYPE_BIOMETRIC_MESSAGE); 554 } 555 if (!TextUtils.isEmpty(mBiometricMessageFollowUp)) { 556 mRotateTextViewController.updateIndication( 557 INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, 558 new KeyguardIndication.Builder() 559 .setMessage(mBiometricMessageFollowUp) 560 .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION) 561 .setTextColor(mInitialTextColorState) 562 .build(), 563 true 564 ); 565 } else { 566 mRotateTextViewController.hideIndication( 567 INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP); 568 } 569 } 570 updateTransient()571 private void updateTransient() { 572 if (mDozing) { 573 updateDeviceEntryIndication(false); 574 return; 575 } 576 577 if (!TextUtils.isEmpty(mTransientIndication)) { 578 mRotateTextViewController.showTransient(mTransientIndication); 579 } else { 580 mRotateTextViewController.hideTransient(); 581 } 582 } 583 updateLockScreenTrustMsg(int userId, CharSequence trustGrantedIndication, CharSequence trustManagedIndication)584 private void updateLockScreenTrustMsg(int userId, CharSequence trustGrantedIndication, 585 CharSequence trustManagedIndication) { 586 final boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(userId); 587 if (!TextUtils.isEmpty(trustGrantedIndication) && userHasTrust) { 588 mRotateTextViewController.updateIndication( 589 INDICATION_TYPE_TRUST, 590 new KeyguardIndication.Builder() 591 .setMessage(trustGrantedIndication) 592 .setTextColor(mInitialTextColorState) 593 .build(), 594 true); 595 hideBiometricMessage(); 596 } else if (!TextUtils.isEmpty(trustManagedIndication) 597 && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) 598 && !userHasTrust) { 599 mRotateTextViewController.updateIndication( 600 INDICATION_TYPE_TRUST, 601 new KeyguardIndication.Builder() 602 .setMessage(trustManagedIndication) 603 .setTextColor(mInitialTextColorState) 604 .build(), 605 false); 606 } else { 607 mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST); 608 } 609 } 610 updateLockScreenAlignmentMsg()611 private void updateLockScreenAlignmentMsg() { 612 if (!TextUtils.isEmpty(mAlignmentIndication)) { 613 mRotateTextViewController.updateIndication( 614 INDICATION_TYPE_ALIGNMENT, 615 new KeyguardIndication.Builder() 616 .setMessage(mAlignmentIndication) 617 .setTextColor(ColorStateList.valueOf( 618 mContext.getColor(R.color.misalignment_text_color))) 619 .build(), 620 true); 621 } else { 622 mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT); 623 } 624 } 625 updateLockScreenPersistentUnlockMsg()626 private void updateLockScreenPersistentUnlockMsg() { 627 if (!TextUtils.isEmpty(mPersistentUnlockMessage)) { 628 mRotateTextViewController.updateIndication( 629 INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE, 630 new KeyguardIndication.Builder() 631 .setMessage(mPersistentUnlockMessage) 632 .setTextColor(mInitialTextColorState) 633 .build(), 634 true); 635 } else { 636 mRotateTextViewController.hideIndication(INDICATION_TYPE_PERSISTENT_UNLOCK_MESSAGE); 637 } 638 } 639 updateLockScreenLogoutView()640 private void updateLockScreenLogoutView() { 641 final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled() 642 && getCurrentUser() != UserHandle.USER_SYSTEM; 643 if (shouldShowLogout) { 644 mRotateTextViewController.updateIndication( 645 INDICATION_TYPE_LOGOUT, 646 new KeyguardIndication.Builder() 647 .setMessage(mContext.getResources().getString( 648 com.android.internal.R.string.global_action_logout)) 649 .setTextColor(Utils.getColorAttr( 650 mContext, com.android.internal.R.attr.textColorOnAccent)) 651 .setBackground(mContext.getDrawable( 652 com.android.systemui.R.drawable.logout_button_background)) 653 .setClickListener((view) -> { 654 if (mFalsingManager.isFalseTap(LOW_PENALTY)) { 655 return; 656 } 657 mDevicePolicyManager.logoutUser(); 658 }) 659 .build(), 660 false); 661 } else { 662 mRotateTextViewController.hideIndication(INDICATION_TYPE_LOGOUT); 663 } 664 } 665 isOrganizationOwnedDevice()666 private boolean isOrganizationOwnedDevice() { 667 return mDevicePolicyManager.isDeviceManaged() 668 || mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile(); 669 } 670 671 @Nullable getOrganizationOwnedDeviceOrganizationName()672 private CharSequence getOrganizationOwnedDeviceOrganizationName() { 673 if (mDevicePolicyManager.isDeviceManaged()) { 674 return mDevicePolicyManager.getDeviceOwnerOrganizationName(); 675 } else if (mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) { 676 return getWorkProfileOrganizationName(); 677 } 678 return null; 679 } 680 getWorkProfileOrganizationName()681 private CharSequence getWorkProfileOrganizationName() { 682 final int profileId = getWorkProfileUserId(UserHandle.myUserId()); 683 if (profileId == UserHandle.USER_NULL) { 684 return null; 685 } 686 return mDevicePolicyManager.getOrganizationNameForUser(profileId); 687 } 688 getWorkProfileUserId(int userId)689 private int getWorkProfileUserId(int userId) { 690 for (final UserInfo userInfo : mUserManager.getProfiles(userId)) { 691 if (userInfo.isManagedProfile()) { 692 return userInfo.id; 693 } 694 } 695 return UserHandle.USER_NULL; 696 } 697 698 /** 699 * Sets the visibility of keyguard bottom area, and if the indications are updatable. 700 * 701 * @param visible true to make the area visible and update the indication, false otherwise. 702 */ setVisible(boolean visible)703 public void setVisible(boolean visible) { 704 mVisible = visible; 705 mIndicationArea.setVisibility(visible ? VISIBLE : GONE); 706 if (visible) { 707 // If this is called after an error message was already shown, we should not clear it. 708 // Otherwise the error message won't be shown 709 if (!mHideTransientMessageHandler.isScheduled()) { 710 hideTransientIndication(); 711 } 712 updateDeviceEntryIndication(false); 713 } else { 714 // If we unlock and return to keyguard quickly, previous error should not be shown 715 hideTransientIndication(); 716 } 717 } 718 setPersistentUnlockMessage(String persistentUnlockMessage)719 private void setPersistentUnlockMessage(String persistentUnlockMessage) { 720 mPersistentUnlockMessage = persistentUnlockMessage; 721 updateDeviceEntryIndication(false); 722 } 723 724 /** 725 * Returns the indication text indicating that trust has been granted. 726 * 727 * @return an empty string if a trust indication text should not be shown. 728 */ 729 @VisibleForTesting getTrustGrantedIndication()730 String getTrustGrantedIndication() { 731 return mTrustGrantedIndication == null 732 ? mContext.getString(R.string.keyguard_indication_trust_unlocked) 733 : mTrustGrantedIndication.toString(); 734 } 735 736 /** 737 * Sets if the device is plugged in 738 */ 739 @VisibleForTesting setPowerPluggedIn(boolean plugged)740 void setPowerPluggedIn(boolean plugged) { 741 mPowerPluggedIn = plugged; 742 } 743 744 /** 745 * Returns the indication text indicating that trust is currently being managed. 746 * 747 * @return {@code null} or an empty string if a trust managed text should not be shown. 748 */ getTrustManagedIndication()749 private String getTrustManagedIndication() { 750 return null; 751 } 752 753 /** 754 * Hides transient indication in {@param delayMs}. 755 */ hideTransientIndicationDelayed(long delayMs)756 public void hideTransientIndicationDelayed(long delayMs) { 757 mHideTransientMessageHandler.schedule(delayMs, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED); 758 } 759 760 /** 761 * Hides biometric indication in {@param delayMs}. 762 */ hideBiometricMessageDelayed(long delayMs)763 public void hideBiometricMessageDelayed(long delayMs) { 764 mHideBiometricMessageHandler.schedule(delayMs, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED); 765 } 766 767 /** 768 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 769 */ showTransientIndication(int transientIndication)770 public void showTransientIndication(int transientIndication) { 771 showTransientIndication(mContext.getResources().getString(transientIndication)); 772 } 773 774 /** 775 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 776 */ showTransientIndication(CharSequence transientIndication)777 private void showTransientIndication(CharSequence transientIndication) { 778 mTransientIndication = transientIndication; 779 hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS); 780 781 updateTransient(); 782 } 783 showBiometricMessage(CharSequence biometricMessage)784 private void showBiometricMessage(CharSequence biometricMessage) { 785 showBiometricMessage(biometricMessage, null); 786 } 787 788 /** 789 * Shows {@param biometricMessage} and {@param biometricMessageFollowUp} 790 * until they are hidden by {@link #hideBiometricMessage}. Messages are rotated through 791 * by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message 792 * logic. 793 */ showBiometricMessage(CharSequence biometricMessage, @Nullable CharSequence biometricMessageFollowUp)794 private void showBiometricMessage(CharSequence biometricMessage, 795 @Nullable CharSequence biometricMessageFollowUp) { 796 if (TextUtils.equals(biometricMessage, mBiometricMessage) 797 && TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) { 798 return; 799 } 800 801 mBiometricMessage = biometricMessage; 802 mBiometricMessageFollowUp = biometricMessageFollowUp; 803 804 mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK); 805 hideBiometricMessageDelayed( 806 !TextUtils.isEmpty(mBiometricMessage) 807 && !TextUtils.isEmpty(mBiometricMessageFollowUp) 808 ? IMPORTANT_MSG_MIN_DURATION * 2 809 : DEFAULT_HIDE_DELAY_MS 810 ); 811 812 updateBiometricMessage(); 813 } 814 hideBiometricMessage()815 private void hideBiometricMessage() { 816 if (mBiometricMessage != null || mBiometricMessageFollowUp != null) { 817 mBiometricMessage = null; 818 mBiometricMessageFollowUp = null; 819 mHideBiometricMessageHandler.cancel(); 820 updateBiometricMessage(); 821 } 822 } 823 824 /** 825 * Hides transient indication. 826 */ hideTransientIndication()827 public void hideTransientIndication() { 828 if (mTransientIndication != null) { 829 mTransientIndication = null; 830 mHideTransientMessageHandler.cancel(); 831 updateTransient(); 832 } 833 } 834 835 /** 836 * Updates message shown to the user. If the device is dozing, a single message with the highest 837 * precedence is shown. If the device is not dozing (on the lock screen), then several messages 838 * may continuously be cycled through. 839 */ updateDeviceEntryIndication(boolean animate)840 protected final void updateDeviceEntryIndication(boolean animate) { 841 mKeyguardLogger.logUpdateDeviceEntryIndication(animate, mVisible, mDozing); 842 if (!mVisible) { 843 return; 844 } 845 846 // A few places might need to hide the indication, so always start by making it visible 847 mIndicationArea.setVisibility(VISIBLE); 848 849 // Walk down a precedence-ordered list of what indication 850 // should be shown based on device state 851 if (mDozing) { 852 mLockScreenIndicationView.setVisibility(View.GONE); 853 mTopIndicationView.setVisibility(VISIBLE); 854 // When dozing we ignore any text color and use white instead, because 855 // colors can be hard to read in low brightness. 856 mTopIndicationView.setTextColor(Color.WHITE); 857 858 CharSequence newIndication; 859 if (!TextUtils.isEmpty(mBiometricMessage)) { 860 newIndication = mBiometricMessage; // note: doesn't show mBiometricMessageFollowUp 861 } else if (!TextUtils.isEmpty(mTransientIndication)) { 862 newIndication = mTransientIndication; 863 } else if (!mBatteryPresent) { 864 // If there is no battery detected, hide the indication and bail 865 mIndicationArea.setVisibility(GONE); 866 return; 867 } else if (!TextUtils.isEmpty(mAlignmentIndication)) { 868 newIndication = mAlignmentIndication; 869 mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); 870 } else if (mPowerPluggedIn || mEnableBatteryDefender) { 871 newIndication = computePowerIndication(); 872 } else { 873 newIndication = NumberFormat.getPercentInstance() 874 .format(mBatteryLevel / 100f); 875 } 876 877 if (!TextUtils.equals(mTopIndicationView.getText(), newIndication)) { 878 mWakeLock.setAcquired(true); 879 mTopIndicationView.switchIndication(newIndication, null, 880 true, () -> mWakeLock.setAcquired(false)); 881 } 882 return; 883 } 884 885 // LOCK SCREEN 886 mTopIndicationView.setVisibility(GONE); 887 mTopIndicationView.setText(null); 888 mLockScreenIndicationView.setVisibility(View.VISIBLE); 889 updateLockScreenIndications(animate, getCurrentUser()); 890 } 891 892 /** 893 * Assumption: device is charging 894 */ computePowerIndication()895 protected String computePowerIndication() { 896 int chargingId; 897 if (mBatteryOverheated) { 898 chargingId = R.string.keyguard_plugged_in_charging_limited; 899 String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); 900 return mContext.getResources().getString(chargingId, percentage); 901 } else if (mPowerCharged) { 902 return mContext.getResources().getString(R.string.keyguard_charged); 903 } 904 905 final boolean hasChargingTime = mChargingTimeRemaining > 0; 906 if (mPowerPluggedInWired) { 907 switch (mChargingSpeed) { 908 case BatteryStatus.CHARGING_FAST: 909 chargingId = hasChargingTime 910 ? R.string.keyguard_indication_charging_time_fast 911 : R.string.keyguard_plugged_in_charging_fast; 912 break; 913 case BatteryStatus.CHARGING_SLOWLY: 914 chargingId = hasChargingTime 915 ? R.string.keyguard_indication_charging_time_slowly 916 : R.string.keyguard_plugged_in_charging_slowly; 917 break; 918 default: 919 chargingId = hasChargingTime 920 ? R.string.keyguard_indication_charging_time 921 : R.string.keyguard_plugged_in; 922 break; 923 } 924 } else if (mPowerPluggedInWireless) { 925 chargingId = hasChargingTime 926 ? R.string.keyguard_indication_charging_time_wireless 927 : R.string.keyguard_plugged_in_wireless; 928 } else if (mPowerPluggedInDock) { 929 chargingId = hasChargingTime 930 ? R.string.keyguard_indication_charging_time_dock 931 : R.string.keyguard_plugged_in_dock; 932 } else { 933 chargingId = hasChargingTime 934 ? R.string.keyguard_indication_charging_time 935 : R.string.keyguard_plugged_in; 936 } 937 938 String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); 939 if (hasChargingTime) { 940 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( 941 mContext, mChargingTimeRemaining); 942 return mContext.getResources().getString(chargingId, chargingTimeFormatted, 943 percentage); 944 } else { 945 return mContext.getResources().getString(chargingId, percentage); 946 } 947 } 948 setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)949 public void setStatusBarKeyguardViewManager( 950 StatusBarKeyguardViewManager statusBarKeyguardViewManager) { 951 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 952 } 953 954 /** 955 * Show message on the keyguard for how the user can unlock/enter their device. 956 */ showActionToUnlock()957 public void showActionToUnlock() { 958 if (mDozing 959 && !mKeyguardUpdateMonitor.getUserCanSkipBouncer( 960 getCurrentUser())) { 961 return; 962 } 963 964 if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 965 if (mAlternateBouncerInteractor.isVisibleState()) { 966 return; // udfps affordance is highlighted, no need to show action to unlock 967 } else if (mKeyguardUpdateMonitor.isFaceEnrolled() 968 && !mKeyguardUpdateMonitor.getIsFaceAuthenticated()) { 969 String message = mContext.getString(R.string.keyguard_retry); 970 mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState); 971 } 972 } else { 973 final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer( 974 getCurrentUser()); 975 if (canSkipBouncer) { 976 final boolean faceAuthenticated = mKeyguardUpdateMonitor.getIsFaceAuthenticated(); 977 final boolean udfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported(); 978 final boolean a11yEnabled = mAccessibilityManager.isEnabled() 979 || mAccessibilityManager.isTouchExplorationEnabled(); 980 if (udfpsSupported && faceAuthenticated) { // co-ex 981 if (a11yEnabled) { 982 showBiometricMessage( 983 mContext.getString(R.string.keyguard_face_successful_unlock), 984 mContext.getString(R.string.keyguard_unlock) 985 ); 986 } else { 987 showBiometricMessage( 988 mContext.getString(R.string.keyguard_face_successful_unlock), 989 mContext.getString(R.string.keyguard_unlock_press) 990 ); 991 } 992 } else if (faceAuthenticated) { // face-only 993 showBiometricMessage( 994 mContext.getString(R.string.keyguard_face_successful_unlock), 995 mContext.getString(R.string.keyguard_unlock) 996 ); 997 } else if (udfpsSupported) { // udfps-only 998 if (a11yEnabled) { 999 showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); 1000 } else { 1001 showBiometricMessage(mContext.getString( 1002 R.string.keyguard_unlock_press)); 1003 } 1004 } else { // no security or unlocked by a trust agent 1005 showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); 1006 } 1007 } else { 1008 // suggest swiping up for the primary authentication bouncer 1009 showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); 1010 } 1011 } 1012 } 1013 dump(PrintWriter pw, String[] args)1014 public void dump(PrintWriter pw, String[] args) { 1015 pw.println("KeyguardIndicationController:"); 1016 pw.println(" mInitialTextColorState: " + mInitialTextColorState); 1017 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired); 1018 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn); 1019 pw.println(" mPowerCharged: " + mPowerCharged); 1020 pw.println(" mChargingSpeed: " + mChargingSpeed); 1021 pw.println(" mChargingWattage: " + mChargingWattage); 1022 pw.println(" mMessageToShowOnScreenOn: " + mBiometricErrorMessageToShowOnScreenOn); 1023 pw.println(" mDozing: " + mDozing); 1024 pw.println(" mTransientIndication: " + mTransientIndication); 1025 pw.println(" mBiometricMessage: " + mBiometricMessage); 1026 pw.println(" mBiometricMessageFollowUp: " + mBiometricMessageFollowUp); 1027 pw.println(" mBatteryLevel: " + mBatteryLevel); 1028 pw.println(" mBatteryPresent: " + mBatteryPresent); 1029 pw.println(" AOD text: " + ( 1030 mTopIndicationView == null ? null : mTopIndicationView.getText())); 1031 pw.println(" computePowerIndication(): " + computePowerIndication()); 1032 pw.println(" trustGrantedIndication: " + getTrustGrantedIndication()); 1033 pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceAcquisitionMsgIdsToShow); 1034 mRotateTextViewController.dump(pw, args); 1035 } 1036 1037 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { 1038 @Override onTimeChanged()1039 public void onTimeChanged() { 1040 if (mVisible) { 1041 updateDeviceEntryIndication(false /* animate */); 1042 } 1043 } 1044 1045 /** 1046 * KeyguardUpdateMonitor only sends "interesting" battery updates 1047 * {@link KeyguardUpdateMonitor#isBatteryUpdateInteresting}. 1048 * Therefore, make sure to always check plugged in state along with any charging status 1049 * change, or else we could end up with stale state. 1050 */ 1051 @Override onRefreshBatteryInfo(BatteryStatus status)1052 public void onRefreshBatteryInfo(BatteryStatus status) { 1053 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING 1054 || status.isCharged(); 1055 boolean wasPluggedIn = mPowerPluggedIn; 1056 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull; 1057 mPowerPluggedInWireless = status.isPluggedInWireless() && isChargingOrFull; 1058 mPowerPluggedInDock = status.isPluggedInDock() && isChargingOrFull; 1059 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; 1060 mPowerCharged = status.isCharged(); 1061 mChargingWattage = status.maxChargingWattage; 1062 mChargingSpeed = status.getChargingSpeed(mContext); 1063 mBatteryLevel = status.level; 1064 mBatteryPresent = status.present; 1065 mBatteryOverheated = status.isOverheated(); 1066 // when the battery is overheated, device doesn't charge so only guard on pluggedIn: 1067 mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn(); 1068 1069 try { 1070 mChargingTimeRemaining = mPowerPluggedIn 1071 ? mBatteryInfo.computeChargeTimeRemaining() : -1; 1072 } catch (RemoteException e) { 1073 mKeyguardLogger.log(TAG, ERROR, "Error calling IBatteryStats", e); 1074 mChargingTimeRemaining = -1; 1075 } 1076 1077 mKeyguardLogger.logRefreshBatteryInfo(isChargingOrFull, mPowerPluggedIn, mBatteryLevel, 1078 mBatteryOverheated); 1079 updateDeviceEntryIndication(!wasPluggedIn && mPowerPluggedInWired); 1080 } 1081 1082 @Override onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo)1083 public void onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo) { 1084 if (biometricSourceType == FACE) { 1085 mFaceAcquiredMessageDeferral.processFrame(acquireInfo); 1086 } 1087 } 1088 1089 @Override onBiometricHelp(int msgId, String helpString, BiometricSourceType biometricSourceType)1090 public void onBiometricHelp(int msgId, String helpString, 1091 BiometricSourceType biometricSourceType) { 1092 if (biometricSourceType == FACE) { 1093 mFaceAcquiredMessageDeferral.updateMessage(msgId, helpString); 1094 if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) { 1095 return; 1096 } 1097 } 1098 1099 final boolean faceAuthUnavailable = biometricSourceType == FACE 1100 && msgId == BIOMETRIC_HELP_FACE_NOT_AVAILABLE; 1101 1102 // TODO(b/141025588): refactor to reduce repetition of code/comments 1103 // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong 1104 // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to 1105 // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the 1106 // check of whether non-strong biometric is allowed 1107 if (!mKeyguardUpdateMonitor 1108 .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) 1109 && !faceAuthUnavailable) { 1110 return; 1111 } 1112 1113 final boolean faceAuthSoftError = biometricSourceType == FACE 1114 && msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED 1115 && msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE; 1116 final boolean faceAuthFailed = biometricSourceType == FACE 1117 && msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed 1118 final boolean fpAuthFailed = biometricSourceType == FINGERPRINT 1119 && msgId == BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; // ran matcher & failed 1120 final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint(); 1121 final boolean isCoExFaceAcquisitionMessage = 1122 faceAuthSoftError && isUnlockWithFingerprintPossible; 1123 if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) { 1124 mKeyguardLogger.logBiometricMessage( 1125 "skipped showing help message due to co-ex logic", 1126 msgId, 1127 helpString); 1128 } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 1129 mStatusBarKeyguardViewManager.setKeyguardMessage(helpString, 1130 mInitialTextColorState); 1131 } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) { 1132 if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) { 1133 showBiometricMessage( 1134 helpString, 1135 mContext.getString(R.string.keyguard_suggest_fingerprint) 1136 ); 1137 } else if (faceAuthFailed && isUnlockWithFingerprintPossible) { 1138 showBiometricMessage( 1139 mContext.getString(R.string.keyguard_face_failed), 1140 mContext.getString(R.string.keyguard_suggest_fingerprint) 1141 ); 1142 } else if (fpAuthFailed 1143 && mKeyguardUpdateMonitor.getUserUnlockedWithFace(getCurrentUser())) { 1144 // face had already previously unlocked the device, so instead of showing a 1145 // fingerprint error, tell them they have already unlocked with face auth 1146 // and how to enter their device 1147 showBiometricMessage( 1148 mContext.getString(R.string.keyguard_face_successful_unlock), 1149 mContext.getString(R.string.keyguard_unlock) 1150 ); 1151 } else if (fpAuthFailed 1152 && mKeyguardUpdateMonitor.getUserHasTrust( 1153 KeyguardUpdateMonitor.getCurrentUser())) { 1154 showBiometricMessage( 1155 getTrustGrantedIndication(), 1156 mContext.getString(R.string.keyguard_unlock) 1157 ); 1158 } else if (faceAuthUnavailable) { 1159 showBiometricMessage( 1160 helpString, 1161 isUnlockWithFingerprintPossible 1162 ? mContext.getString(R.string.keyguard_suggest_fingerprint) 1163 : mContext.getString(R.string.keyguard_unlock) 1164 ); 1165 } else { 1166 showBiometricMessage(helpString); 1167 } 1168 } else if (faceAuthFailed) { 1169 // show action to unlock 1170 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK), 1171 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); 1172 } else { 1173 mBiometricErrorMessageToShowOnScreenOn = helpString; 1174 mHandler.sendMessageDelayed( 1175 mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON), 1176 1000); 1177 } 1178 } 1179 1180 @Override onBiometricAuthFailed(BiometricSourceType biometricSourceType)1181 public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { 1182 if (biometricSourceType == FACE) { 1183 mFaceAcquiredMessageDeferral.reset(); 1184 } 1185 } 1186 1187 @Override onLockedOutStateChanged(BiometricSourceType biometricSourceType)1188 public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) { 1189 if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) { 1190 mFaceLockedOutThisAuthSession = false; 1191 } else if (biometricSourceType == FINGERPRINT) { 1192 setPersistentUnlockMessage(mKeyguardUpdateMonitor.isFingerprintLockedOut() 1193 ? mContext.getString(R.string.keyguard_unlock) : ""); 1194 } 1195 } 1196 1197 @Override onBiometricError(int msgId, String errString, BiometricSourceType biometricSourceType)1198 public void onBiometricError(int msgId, String errString, 1199 BiometricSourceType biometricSourceType) { 1200 if (biometricSourceType == FACE) { 1201 onFaceAuthError(msgId, errString); 1202 } else if (biometricSourceType == FINGERPRINT) { 1203 onFingerprintAuthError(msgId, errString); 1204 } 1205 } 1206 onFaceAuthError(int msgId, String errString)1207 private void onFaceAuthError(int msgId, String errString) { 1208 CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage(); 1209 mFaceAcquiredMessageDeferral.reset(); 1210 if (shouldSuppressFaceError(msgId, mKeyguardUpdateMonitor)) { 1211 mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString); 1212 return; 1213 } 1214 if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { 1215 handleFaceAuthTimeoutError(deferredFaceMessage); 1216 } else if (isLockoutError(msgId)) { 1217 handleFaceLockoutError(errString); 1218 } else { 1219 showErrorMessageNowOrLater(errString, null); 1220 } 1221 } 1222 onFingerprintAuthError(int msgId, String errString)1223 private void onFingerprintAuthError(int msgId, String errString) { 1224 if (shouldSuppressFingerprintError(msgId, mKeyguardUpdateMonitor)) { 1225 mKeyguardLogger.logBiometricMessage("suppressingFingerprintError", 1226 msgId, 1227 errString); 1228 } else { 1229 showErrorMessageNowOrLater(errString, null); 1230 } 1231 } 1232 shouldSuppressFingerprintError(int msgId, KeyguardUpdateMonitor updateMonitor)1233 private boolean shouldSuppressFingerprintError(int msgId, 1234 KeyguardUpdateMonitor updateMonitor) { 1235 // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong 1236 // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to 1237 // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the 1238 // check of whether non-strong biometric is allowed 1239 return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) 1240 && !isLockoutError(msgId)) 1241 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED 1242 || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED 1243 || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED); 1244 } 1245 shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor)1246 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) { 1247 // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong 1248 // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to 1249 // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the 1250 // check of whether non-strong biometric is allowed 1251 return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) 1252 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) 1253 || msgId == FaceManager.FACE_ERROR_CANCELED 1254 || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS); 1255 } 1256 1257 1258 @Override onTrustChanged(int userId)1259 public void onTrustChanged(int userId) { 1260 if (!isCurrentUser(userId)) return; 1261 updateDeviceEntryIndication(false); 1262 } 1263 1264 @Override onTrustGrantedForCurrentUser( boolean dismissKeyguard, boolean newlyUnlocked, @NonNull TrustGrantFlags flags, @Nullable String message )1265 public void onTrustGrantedForCurrentUser( 1266 boolean dismissKeyguard, 1267 boolean newlyUnlocked, 1268 @NonNull TrustGrantFlags flags, 1269 @Nullable String message 1270 ) { 1271 showTrustGrantedMessage(dismissKeyguard, message); 1272 } 1273 1274 @Override onTrustAgentErrorMessage(CharSequence message)1275 public void onTrustAgentErrorMessage(CharSequence message) { 1276 showBiometricMessage(message); 1277 } 1278 1279 @Override onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType)1280 public void onBiometricRunningStateChanged(boolean running, 1281 BiometricSourceType biometricSourceType) { 1282 if (running && biometricSourceType == FACE) { 1283 // Let's hide any previous messages when authentication starts, otherwise 1284 // multiple auth attempts would overlap. 1285 hideBiometricMessage(); 1286 mBiometricErrorMessageToShowOnScreenOn = null; 1287 } 1288 } 1289 1290 @Override onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, boolean isStrongBiometric)1291 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, 1292 boolean isStrongBiometric) { 1293 super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric); 1294 hideBiometricMessage(); 1295 if (biometricSourceType == FACE) { 1296 mFaceAcquiredMessageDeferral.reset(); 1297 if (!mKeyguardBypassController.canBypass()) { 1298 showActionToUnlock(); 1299 } 1300 } 1301 } 1302 1303 @Override onUserSwitchComplete(int userId)1304 public void onUserSwitchComplete(int userId) { 1305 if (mVisible) { 1306 updateDeviceEntryIndication(false); 1307 } 1308 } 1309 1310 @Override onUserUnlocked()1311 public void onUserUnlocked() { 1312 if (mVisible) { 1313 updateDeviceEntryIndication(false); 1314 } 1315 } 1316 1317 @Override onLogoutEnabledChanged()1318 public void onLogoutEnabledChanged() { 1319 if (mVisible) { 1320 updateDeviceEntryIndication(false); 1321 } 1322 } 1323 1324 @Override onRequireUnlockForNfc()1325 public void onRequireUnlockForNfc() { 1326 showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc)); 1327 hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS); 1328 } 1329 } 1330 isPluggedInAndCharging()1331 protected boolean isPluggedInAndCharging() { 1332 return mPowerPluggedIn; 1333 } 1334 isCurrentUser(int userId)1335 private boolean isCurrentUser(int userId) { 1336 return getCurrentUser() == userId; 1337 } 1338 showTrustGrantedMessage(boolean dismissKeyguard, @Nullable String message)1339 protected void showTrustGrantedMessage(boolean dismissKeyguard, @Nullable String message) { 1340 mTrustGrantedIndication = message; 1341 updateDeviceEntryIndication(false); 1342 } 1343 handleFaceLockoutError(String errString)1344 private void handleFaceLockoutError(String errString) { 1345 String followupMessage = faceLockedOutFollowupMessage(); 1346 // Lockout error can happen multiple times in a session because we trigger face auth 1347 // even when it is locked out so that the user is aware that face unlock would have 1348 // triggered but didn't because it is locked out. 1349 1350 // On first lockout we show the error message from FaceManager, which tells the user they 1351 // had too many unsuccessful attempts. 1352 if (!mFaceLockedOutThisAuthSession) { 1353 mFaceLockedOutThisAuthSession = true; 1354 showErrorMessageNowOrLater(errString, followupMessage); 1355 } else if (!mAuthController.isUdfpsFingerDown()) { 1356 // On subsequent lockouts, we show a more generic locked out message. 1357 showErrorMessageNowOrLater( 1358 mContext.getString(R.string.keyguard_face_unlock_unavailable), 1359 followupMessage); 1360 } 1361 } 1362 faceLockedOutFollowupMessage()1363 private String faceLockedOutFollowupMessage() { 1364 int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint 1365 : R.string.keyguard_unlock; 1366 return mContext.getString(followupMsgId); 1367 } 1368 isLockoutError(int msgId)1369 private static boolean isLockoutError(int msgId) { 1370 return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT 1371 || msgId == FaceManager.FACE_ERROR_LOCKOUT; 1372 } 1373 handleFaceAuthTimeoutError(@ullable CharSequence deferredFaceMessage)1374 private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) { 1375 mKeyguardLogger.logBiometricMessage("deferred message after face auth timeout", 1376 null, String.valueOf(deferredFaceMessage)); 1377 if (canUnlockWithFingerprint()) { 1378 // Co-ex: show deferred message OR nothing 1379 // if we're on the lock screen (bouncer isn't showing), show the deferred msg 1380 if (deferredFaceMessage != null 1381 && !mStatusBarKeyguardViewManager.isBouncerShowing()) { 1382 showBiometricMessage( 1383 deferredFaceMessage, 1384 mContext.getString(R.string.keyguard_suggest_fingerprint) 1385 ); 1386 } else { 1387 // otherwise, don't show any message 1388 mKeyguardLogger.logBiometricMessage( 1389 "skip showing FACE_ERROR_TIMEOUT due to co-ex logic"); 1390 } 1391 } else if (deferredFaceMessage != null) { 1392 // Face-only: The face timeout message is not very actionable, let's ask the 1393 // user to manually retry. 1394 showBiometricMessage( 1395 deferredFaceMessage, 1396 mContext.getString(R.string.keyguard_unlock) 1397 ); 1398 } else { 1399 // Face-only 1400 // suggest swiping up to unlock (try face auth again or swipe up to bouncer) 1401 showActionToUnlock(); 1402 } 1403 } 1404 canUnlockWithFingerprint()1405 private boolean canUnlockWithFingerprint() { 1406 return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( 1407 KeyguardUpdateMonitor.getCurrentUser()); 1408 } 1409 showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg)1410 private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) { 1411 if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 1412 mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState); 1413 } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) { 1414 showBiometricMessage(errString, followUpMsg); 1415 } else { 1416 mBiometricErrorMessageToShowOnScreenOn = errString; 1417 } 1418 } 1419 1420 private final StatusBarStateController.StateListener mStatusBarStateListener = 1421 new StatusBarStateController.StateListener() { 1422 @Override 1423 public void onStateChanged(int newState) { 1424 setVisible(newState == StatusBarState.KEYGUARD); 1425 } 1426 1427 @Override 1428 public void onDozingChanged(boolean dozing) { 1429 if (mDozing == dozing) { 1430 return; 1431 } 1432 mDozing = dozing; 1433 1434 if (mDozing) { 1435 hideBiometricMessage(); 1436 } 1437 updateDeviceEntryIndication(false); 1438 } 1439 }; 1440 1441 private final KeyguardStateController.Callback mKeyguardStateCallback = 1442 new KeyguardStateController.Callback() { 1443 @Override 1444 public void onUnlockedChanged() { 1445 updateDeviceEntryIndication(false); 1446 } 1447 1448 @Override 1449 public void onKeyguardShowingChanged() { 1450 // All transient messages are gone the next time keyguard is shown 1451 if (!mKeyguardStateController.isShowing()) { 1452 mKeyguardLogger.log(TAG, LogLevel.DEBUG, "clear messages"); 1453 mTopIndicationView.clearMessages(); 1454 mRotateTextViewController.clearMessages(); 1455 } else { 1456 updateDeviceEntryIndication(false); 1457 } 1458 } 1459 }; 1460 } 1461