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