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.view.View.GONE; 21 import static android.view.View.VISIBLE; 22 23 import static com.android.systemui.DejankUtils.whitelistIpcs; 24 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION; 25 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; 26 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; 27 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; 28 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; 29 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; 30 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; 31 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING; 32 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; 33 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; 34 import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; 35 36 import android.animation.Animator; 37 import android.animation.AnimatorListenerAdapter; 38 import android.app.IActivityManager; 39 import android.app.admin.DevicePolicyManager; 40 import android.content.BroadcastReceiver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.pm.UserInfo; 45 import android.content.res.ColorStateList; 46 import android.content.res.Resources; 47 import android.graphics.Color; 48 import android.hardware.biometrics.BiometricSourceType; 49 import android.hardware.face.FaceManager; 50 import android.hardware.fingerprint.FingerprintManager; 51 import android.os.BatteryManager; 52 import android.os.Handler; 53 import android.os.Message; 54 import android.os.RemoteException; 55 import android.os.UserHandle; 56 import android.os.UserManager; 57 import android.text.TextUtils; 58 import android.text.format.Formatter; 59 import android.util.Log; 60 import android.view.View; 61 import android.view.ViewGroup; 62 63 import androidx.annotation.Nullable; 64 65 import com.android.internal.annotations.VisibleForTesting; 66 import com.android.internal.app.IBatteryStats; 67 import com.android.internal.widget.LockPatternUtils; 68 import com.android.internal.widget.ViewClippingUtil; 69 import com.android.keyguard.KeyguardUpdateMonitor; 70 import com.android.keyguard.KeyguardUpdateMonitorCallback; 71 import com.android.settingslib.Utils; 72 import com.android.settingslib.fuelgauge.BatteryStatus; 73 import com.android.systemui.R; 74 import com.android.systemui.animation.Interpolators; 75 import com.android.systemui.broadcast.BroadcastDispatcher; 76 import com.android.systemui.dagger.SysUISingleton; 77 import com.android.systemui.dagger.qualifiers.Main; 78 import com.android.systemui.dock.DockManager; 79 import com.android.systemui.keyguard.KeyguardIndication; 80 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; 81 import com.android.systemui.plugins.FalsingManager; 82 import com.android.systemui.plugins.statusbar.StatusBarStateController; 83 import com.android.systemui.statusbar.phone.KeyguardBypassController; 84 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; 85 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 86 import com.android.systemui.statusbar.policy.KeyguardStateController; 87 import com.android.systemui.util.concurrency.DelayableExecutor; 88 import com.android.systemui.util.wakelock.SettableWakeLock; 89 import com.android.systemui.util.wakelock.WakeLock; 90 91 import java.io.FileDescriptor; 92 import java.io.PrintWriter; 93 import java.text.NumberFormat; 94 95 import javax.inject.Inject; 96 97 /** 98 * Controls the indications and error messages shown on the Keyguard 99 * 100 * On AoD, only one message shows with the following priorities: 101 * 1. Biometric 102 * 2. Transient 103 * 3. Charging alignment 104 * 4. Battery information 105 * 106 * On the lock screen, message rotate through different message types. 107 * See {@link KeyguardIndicationRotateTextViewController.IndicationType} for the list of types. 108 */ 109 @SysUISingleton 110 public class KeyguardIndicationController { 111 112 private static final String TAG = "KeyguardIndication"; 113 private static final boolean DEBUG_CHARGING_SPEED = false; 114 115 private static final int MSG_HIDE_TRANSIENT = 1; 116 private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2; 117 private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3; 118 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; 119 private static final float BOUNCE_ANIMATION_FINAL_Y = 0f; 120 121 private final Context mContext; 122 private final BroadcastDispatcher mBroadcastDispatcher; 123 private final KeyguardStateController mKeyguardStateController; 124 private final StatusBarStateController mStatusBarStateController; 125 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 126 private ViewGroup mIndicationArea; 127 private KeyguardIndicationTextView mTopIndicationView; 128 private KeyguardIndicationTextView mLockScreenIndicationView; 129 private final IBatteryStats mBatteryInfo; 130 private final SettableWakeLock mWakeLock; 131 private final DockManager mDockManager; 132 private final DevicePolicyManager mDevicePolicyManager; 133 private final UserManager mUserManager; 134 private final @Main DelayableExecutor mExecutor; 135 private final LockPatternUtils mLockPatternUtils; 136 private final IActivityManager mIActivityManager; 137 private final FalsingManager mFalsingManager; 138 private final KeyguardBypassController mKeyguardBypassController; 139 140 protected KeyguardIndicationRotateTextViewController mRotateTextViewController; 141 private BroadcastReceiver mBroadcastReceiver; 142 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 143 144 private String mRestingIndication; 145 private String mAlignmentIndication; 146 private CharSequence mTransientIndication; 147 private CharSequence mBiometricMessage; 148 protected ColorStateList mInitialTextColorState; 149 private boolean mVisible; 150 151 private boolean mPowerPluggedIn; 152 private boolean mPowerPluggedInWired; 153 private boolean mPowerPluggedInWireless; 154 private boolean mPowerCharged; 155 private boolean mBatteryOverheated; 156 private boolean mEnableBatteryDefender; 157 private int mChargingSpeed; 158 private int mChargingWattage; 159 private int mBatteryLevel; 160 private boolean mBatteryPresent = true; 161 private long mChargingTimeRemaining; 162 private String mMessageToShowOnScreenOn; 163 private boolean mInited; 164 165 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; 166 167 private boolean mDozing; 168 private final ViewClippingUtil.ClippingParameters mClippingParams = 169 new ViewClippingUtil.ClippingParameters() { 170 @Override 171 public boolean shouldFinish(View view) { 172 return view == mIndicationArea; 173 } 174 }; 175 176 /** 177 * Creates a new KeyguardIndicationController and registers callbacks. 178 */ 179 @Inject KeyguardIndicationController(Context context, WakeLock.Builder wakeLockBuilder, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, BroadcastDispatcher broadcastDispatcher, DevicePolicyManager devicePolicyManager, IBatteryStats iBatteryStats, UserManager userManager, @Main DelayableExecutor executor, FalsingManager falsingManager, LockPatternUtils lockPatternUtils, IActivityManager iActivityManager, KeyguardBypassController keyguardBypassController)180 public KeyguardIndicationController(Context context, 181 WakeLock.Builder wakeLockBuilder, 182 KeyguardStateController keyguardStateController, 183 StatusBarStateController statusBarStateController, 184 KeyguardUpdateMonitor keyguardUpdateMonitor, 185 DockManager dockManager, 186 BroadcastDispatcher broadcastDispatcher, 187 DevicePolicyManager devicePolicyManager, 188 IBatteryStats iBatteryStats, 189 UserManager userManager, 190 @Main DelayableExecutor executor, 191 FalsingManager falsingManager, 192 LockPatternUtils lockPatternUtils, 193 IActivityManager iActivityManager, 194 KeyguardBypassController keyguardBypassController) { 195 mContext = context; 196 mBroadcastDispatcher = broadcastDispatcher; 197 mDevicePolicyManager = devicePolicyManager; 198 mKeyguardStateController = keyguardStateController; 199 mStatusBarStateController = statusBarStateController; 200 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 201 mDockManager = dockManager; 202 mWakeLock = new SettableWakeLock( 203 wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG); 204 mBatteryInfo = iBatteryStats; 205 mUserManager = userManager; 206 mExecutor = executor; 207 mLockPatternUtils = lockPatternUtils; 208 mIActivityManager = iActivityManager; 209 mFalsingManager = falsingManager; 210 mKeyguardBypassController = keyguardBypassController; 211 212 } 213 214 /** Call this after construction to finish setting up the instance. */ init()215 public void init() { 216 if (mInited) { 217 return; 218 } 219 mInited = true; 220 221 mDockManager.addAlignmentStateListener( 222 alignState -> mHandler.post(() -> handleAlignStateChanged(alignState))); 223 mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); 224 mKeyguardUpdateMonitor.registerCallback(mTickReceiver); 225 mStatusBarStateController.addCallback(mStatusBarStateListener); 226 mKeyguardStateController.addCallback(mKeyguardStateCallback); 227 228 mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing()); 229 } 230 setIndicationArea(ViewGroup indicationArea)231 public void setIndicationArea(ViewGroup indicationArea) { 232 mIndicationArea = indicationArea; 233 mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text); 234 mLockScreenIndicationView = indicationArea.findViewById( 235 R.id.keyguard_indication_text_bottom); 236 mInitialTextColorState = mTopIndicationView != null 237 ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE); 238 mRotateTextViewController = new KeyguardIndicationRotateTextViewController( 239 mLockScreenIndicationView, 240 mExecutor, 241 mStatusBarStateController); 242 updateIndication(false /* animate */); 243 updateDisclosure(); 244 if (mBroadcastReceiver == null) { 245 // Update the disclosure proactively to avoid IPC on the critical path. 246 mBroadcastReceiver = new BroadcastReceiver() { 247 @Override 248 public void onReceive(Context context, Intent intent) { 249 updateDisclosure(); 250 } 251 }; 252 IntentFilter intentFilter = new IntentFilter(); 253 intentFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 254 intentFilter.addAction(Intent.ACTION_USER_REMOVED); 255 mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter); 256 } 257 } 258 handleAlignStateChanged(int alignState)259 private void handleAlignStateChanged(int alignState) { 260 String alignmentIndication = ""; 261 if (alignState == DockManager.ALIGN_STATE_POOR) { 262 alignmentIndication = 263 mContext.getResources().getString(R.string.dock_alignment_slow_charging); 264 } else if (alignState == DockManager.ALIGN_STATE_TERRIBLE) { 265 alignmentIndication = 266 mContext.getResources().getString(R.string.dock_alignment_not_charging); 267 } 268 if (!alignmentIndication.equals(mAlignmentIndication)) { 269 mAlignmentIndication = alignmentIndication; 270 updateIndication(false); 271 } 272 } 273 274 /** 275 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this 276 * {@link KeyguardIndicationController}. 277 * 278 * <p>Subclasses may override this method to extend or change the callback behavior by extending 279 * the {@link BaseKeyguardCallback}. 280 * 281 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the 282 * same instance. 283 */ getKeyguardCallback()284 protected KeyguardUpdateMonitorCallback getKeyguardCallback() { 285 if (mUpdateMonitorCallback == null) { 286 mUpdateMonitorCallback = new BaseKeyguardCallback(); 287 } 288 return mUpdateMonitorCallback; 289 } 290 291 /** 292 * Doesn't include disclosure (also a persistent indication) which gets triggered separately. 293 * 294 * This method also doesn't update transient messages like biometrics since those messages 295 * are also updated separately. 296 */ updatePersistentIndications(boolean animate, int userId)297 private void updatePersistentIndications(boolean animate, int userId) { 298 updateOwnerInfo(); 299 updateBattery(animate); 300 updateUserLocked(userId); 301 updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication()); 302 updateAlignment(); 303 updateLogoutView(); 304 updateResting(); 305 } 306 updateDisclosure()307 private void updateDisclosure() { 308 // avoid calling this method since it has an IPC 309 if (whitelistIpcs(this::isOrganizationOwnedDevice)) { 310 final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); 311 final CharSequence disclosure = getDisclosureText(organizationName); 312 mRotateTextViewController.updateIndication( 313 INDICATION_TYPE_DISCLOSURE, 314 new KeyguardIndication.Builder() 315 .setMessage(disclosure) 316 .setTextColor(mInitialTextColorState) 317 .build(), 318 /* updateImmediately */ false); 319 } else { 320 mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE); 321 } 322 323 updateResting(); 324 } 325 getDisclosureText(@ullable CharSequence organizationName)326 private CharSequence getDisclosureText(@Nullable CharSequence organizationName) { 327 final Resources packageResources = mContext.getResources(); 328 if (organizationName == null) { 329 return packageResources.getText(R.string.do_disclosure_generic); 330 } else if (mDevicePolicyManager.isDeviceManaged() 331 && mDevicePolicyManager.getDeviceOwnerType( 332 mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) 333 == DEVICE_OWNER_TYPE_FINANCED) { 334 return packageResources.getString(R.string.do_financed_disclosure_with_name, 335 organizationName); 336 } else { 337 return packageResources.getString(R.string.do_disclosure_with_name, 338 organizationName); 339 } 340 } 341 updateOwnerInfo()342 private void updateOwnerInfo() { 343 String info = mLockPatternUtils.getDeviceOwnerInfo(); 344 if (info == null) { 345 // Use the current user owner information if enabled. 346 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( 347 KeyguardUpdateMonitor.getCurrentUser()); 348 if (ownerInfoEnabled) { 349 info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); 350 } 351 } 352 if (!TextUtils.isEmpty(info)) { 353 mRotateTextViewController.updateIndication( 354 INDICATION_TYPE_OWNER_INFO, 355 new KeyguardIndication.Builder() 356 .setMessage(info) 357 .setTextColor(mInitialTextColorState) 358 .build(), 359 false); 360 } else { 361 mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO); 362 } 363 } 364 updateBattery(boolean animate)365 private void updateBattery(boolean animate) { 366 if (mPowerPluggedIn || mEnableBatteryDefender) { 367 String powerIndication = computePowerIndication(); 368 if (DEBUG_CHARGING_SPEED) { 369 powerIndication += ", " + (mChargingWattage / 1000) + " mW"; 370 } 371 372 mRotateTextViewController.updateIndication( 373 INDICATION_TYPE_BATTERY, 374 new KeyguardIndication.Builder() 375 .setMessage(powerIndication) 376 .setTextColor(mInitialTextColorState) 377 .build(), 378 animate); 379 } else { 380 // don't show the charging information if device isn't plugged in 381 mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY); 382 } 383 } 384 updateUserLocked(int userId)385 private void updateUserLocked(int userId) { 386 if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { 387 mRotateTextViewController.updateIndication( 388 INDICATION_TYPE_USER_LOCKED, 389 new KeyguardIndication.Builder() 390 .setMessage(mContext.getResources().getText( 391 com.android.internal.R.string.lockscreen_storage_locked)) 392 .setTextColor(mInitialTextColorState) 393 .build(), 394 false); 395 } else { 396 mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED); 397 } 398 } 399 updateBiometricMessage()400 private void updateBiometricMessage() { 401 if (!TextUtils.isEmpty(mBiometricMessage)) { 402 mRotateTextViewController.updateIndication( 403 INDICATION_TYPE_BIOMETRIC_MESSAGE, 404 new KeyguardIndication.Builder() 405 .setMessage(mBiometricMessage) 406 .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION) 407 .setTextColor(mInitialTextColorState) 408 .build(), 409 true 410 ); 411 } else { 412 mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE); 413 } 414 415 if (mDozing) { 416 updateIndication(false); 417 } 418 } 419 updateTransient()420 private void updateTransient() { 421 if (!TextUtils.isEmpty(mTransientIndication)) { 422 mRotateTextViewController.showTransient(mTransientIndication); 423 } else { 424 mRotateTextViewController.hideTransient(); 425 } 426 427 if (mDozing) { 428 updateIndication(false); 429 } 430 } 431 updateTrust(int userId, CharSequence trustGrantedIndication, CharSequence trustManagedIndication)432 private void updateTrust(int userId, CharSequence trustGrantedIndication, 433 CharSequence trustManagedIndication) { 434 if (!TextUtils.isEmpty(trustGrantedIndication) 435 && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { 436 mRotateTextViewController.updateIndication( 437 INDICATION_TYPE_TRUST, 438 new KeyguardIndication.Builder() 439 .setMessage(trustGrantedIndication) 440 .setTextColor(mInitialTextColorState) 441 .build(), 442 false); 443 } else if (!TextUtils.isEmpty(trustManagedIndication) 444 && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) 445 && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { 446 mRotateTextViewController.updateIndication( 447 INDICATION_TYPE_TRUST, 448 new KeyguardIndication.Builder() 449 .setMessage(trustManagedIndication) 450 .setTextColor(mInitialTextColorState) 451 .build(), 452 false); 453 } else { 454 mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST); 455 } 456 } 457 updateAlignment()458 private void updateAlignment() { 459 if (!TextUtils.isEmpty(mAlignmentIndication)) { 460 mRotateTextViewController.updateIndication( 461 INDICATION_TYPE_ALIGNMENT, 462 new KeyguardIndication.Builder() 463 .setMessage(mAlignmentIndication) 464 .setTextColor(ColorStateList.valueOf( 465 mContext.getColor(R.color.misalignment_text_color))) 466 .build(), 467 true); 468 } else { 469 mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT); 470 } 471 } 472 updateResting()473 private void updateResting() { 474 if (!TextUtils.isEmpty(mRestingIndication) 475 && !mRotateTextViewController.hasIndications()) { 476 mRotateTextViewController.updateIndication( 477 INDICATION_TYPE_RESTING, 478 new KeyguardIndication.Builder() 479 .setMessage(mRestingIndication) 480 .setTextColor(mInitialTextColorState) 481 .build(), 482 false); 483 } else { 484 mRotateTextViewController.hideIndication(INDICATION_TYPE_RESTING); 485 } 486 } 487 updateLogoutView()488 private void updateLogoutView() { 489 final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled() 490 && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; 491 if (shouldShowLogout) { 492 mRotateTextViewController.updateIndication( 493 INDICATION_TYPE_LOGOUT, 494 new KeyguardIndication.Builder() 495 .setMessage(mContext.getResources().getString( 496 com.android.internal.R.string.global_action_logout)) 497 .setTextColor(Utils.getColorAttr( 498 mContext, com.android.internal.R.attr.textColorOnAccent)) 499 .setBackground(mContext.getDrawable( 500 com.android.systemui.R.drawable.logout_button_background)) 501 .setClickListener((view) -> { 502 if (mFalsingManager.isFalseTap(LOW_PENALTY)) { 503 return; 504 } 505 int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); 506 try { 507 mIActivityManager.switchUser(UserHandle.USER_SYSTEM); 508 mIActivityManager.stopUser(currentUserId, true /* force */, 509 null); 510 } catch (RemoteException re) { 511 Log.e(TAG, "Failed to logout user", re); 512 } 513 }) 514 .build(), 515 false); 516 } else { 517 mRotateTextViewController.hideIndication(INDICATION_TYPE_LOGOUT); 518 } 519 } 520 isOrganizationOwnedDevice()521 private boolean isOrganizationOwnedDevice() { 522 return mDevicePolicyManager.isDeviceManaged() 523 || mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile(); 524 } 525 526 @Nullable getOrganizationOwnedDeviceOrganizationName()527 private CharSequence getOrganizationOwnedDeviceOrganizationName() { 528 if (mDevicePolicyManager.isDeviceManaged()) { 529 return mDevicePolicyManager.getDeviceOwnerOrganizationName(); 530 } else if (mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) { 531 return getWorkProfileOrganizationName(); 532 } 533 return null; 534 } 535 getWorkProfileOrganizationName()536 private CharSequence getWorkProfileOrganizationName() { 537 final int profileId = getWorkProfileUserId(UserHandle.myUserId()); 538 if (profileId == UserHandle.USER_NULL) { 539 return null; 540 } 541 return mDevicePolicyManager.getOrganizationNameForUser(profileId); 542 } 543 getWorkProfileUserId(int userId)544 private int getWorkProfileUserId(int userId) { 545 for (final UserInfo userInfo : mUserManager.getProfiles(userId)) { 546 if (userInfo.isManagedProfile()) { 547 return userInfo.id; 548 } 549 } 550 return UserHandle.USER_NULL; 551 } 552 553 /** 554 * Sets the visibility of keyguard bottom area, and if the indications are updatable. 555 * 556 * @param visible true to make the area visible and update the indication, false otherwise. 557 */ setVisible(boolean visible)558 public void setVisible(boolean visible) { 559 mVisible = visible; 560 mIndicationArea.setVisibility(visible ? VISIBLE : GONE); 561 if (visible) { 562 // If this is called after an error message was already shown, we should not clear it. 563 // Otherwise the error message won't be shown 564 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) { 565 hideTransientIndication(); 566 } 567 updateIndication(false); 568 } else if (!visible) { 569 // If we unlock and return to keyguard quickly, previous error should not be shown 570 hideTransientIndication(); 571 } 572 } 573 574 /** 575 * Sets the indication that is shown if nothing else is showing. 576 */ setRestingIndication(String restingIndication)577 public void setRestingIndication(String restingIndication) { 578 mRestingIndication = restingIndication; 579 updateIndication(false); 580 } 581 582 /** 583 * Returns the indication text indicating that trust has been granted. 584 * 585 * @return {@code null} or an empty string if a trust indication text should not be shown. 586 */ 587 @VisibleForTesting getTrustGrantedIndication()588 String getTrustGrantedIndication() { 589 return mContext.getString(R.string.keyguard_indication_trust_unlocked); 590 } 591 592 /** 593 * Sets if the device is plugged in 594 */ 595 @VisibleForTesting setPowerPluggedIn(boolean plugged)596 void setPowerPluggedIn(boolean plugged) { 597 mPowerPluggedIn = plugged; 598 } 599 600 /** 601 * Returns the indication text indicating that trust is currently being managed. 602 * 603 * @return {@code null} or an empty string if a trust managed text should not be shown. 604 */ getTrustManagedIndication()605 private String getTrustManagedIndication() { 606 return null; 607 } 608 609 /** 610 * Hides transient indication in {@param delayMs}. 611 */ hideTransientIndicationDelayed(long delayMs)612 public void hideTransientIndicationDelayed(long delayMs) { 613 mHandler.sendMessageDelayed( 614 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs); 615 } 616 617 /** 618 * Hides biometric indication in {@param delayMs}. 619 */ hideBiometricMessageDelayed(long delayMs)620 public void hideBiometricMessageDelayed(long delayMs) { 621 mHandler.sendMessageDelayed( 622 mHandler.obtainMessage(MSG_HIDE_BIOMETRIC_MESSAGE), delayMs); 623 } 624 625 /** 626 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 627 */ showTransientIndication(int transientIndication)628 public void showTransientIndication(int transientIndication) { 629 showTransientIndication(mContext.getResources().getString(transientIndication)); 630 } 631 632 /** 633 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 634 */ showTransientIndication(CharSequence transientIndication)635 private void showTransientIndication(CharSequence transientIndication) { 636 mTransientIndication = transientIndication; 637 mHandler.removeMessages(MSG_HIDE_TRANSIENT); 638 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); 639 640 updateTransient(); 641 } 642 643 /** 644 * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}. 645 */ showBiometricMessage(int biometricMessage)646 public void showBiometricMessage(int biometricMessage) { 647 showBiometricMessage(mContext.getResources().getString(biometricMessage)); 648 } 649 650 /** 651 * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}. 652 */ showBiometricMessage(CharSequence biometricMessage)653 private void showBiometricMessage(CharSequence biometricMessage) { 654 mBiometricMessage = biometricMessage; 655 656 mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK); 657 mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE); 658 hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); 659 660 updateBiometricMessage(); 661 } 662 hideBiometricMessage()663 private void hideBiometricMessage() { 664 if (mBiometricMessage != null) { 665 mBiometricMessage = null; 666 mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE); 667 updateBiometricMessage(); 668 } 669 } 670 671 /** 672 * Hides transient indication. 673 */ hideTransientIndication()674 public void hideTransientIndication() { 675 if (mTransientIndication != null) { 676 mTransientIndication = null; 677 mHandler.removeMessages(MSG_HIDE_TRANSIENT); 678 updateTransient(); 679 } 680 } 681 updateIndication(boolean animate)682 protected final void updateIndication(boolean animate) { 683 if (!mVisible) { 684 return; 685 } 686 687 // A few places might need to hide the indication, so always start by making it visible 688 mIndicationArea.setVisibility(VISIBLE); 689 690 // Walk down a precedence-ordered list of what indication 691 // should be shown based on user or device state 692 // AoD 693 if (mDozing) { 694 mLockScreenIndicationView.setVisibility(View.GONE); 695 mTopIndicationView.setVisibility(VISIBLE); 696 // When dozing we ignore any text color and use white instead, because 697 // colors can be hard to read in low brightness. 698 mTopIndicationView.setTextColor(Color.WHITE); 699 if (!TextUtils.isEmpty(mBiometricMessage)) { 700 mWakeLock.setAcquired(true); 701 mTopIndicationView.switchIndication(mBiometricMessage, null, 702 true, () -> mWakeLock.setAcquired(false)); 703 } else if (!TextUtils.isEmpty(mTransientIndication)) { 704 mWakeLock.setAcquired(true); 705 mTopIndicationView.switchIndication(mTransientIndication, null, 706 true, () -> mWakeLock.setAcquired(false)); 707 } else if (!mBatteryPresent) { 708 // If there is no battery detected, hide the indication and bail 709 mIndicationArea.setVisibility(GONE); 710 } else if (!TextUtils.isEmpty(mAlignmentIndication)) { 711 mTopIndicationView.switchIndication(mAlignmentIndication, null, 712 false /* animate */, null /* onAnimationEndCallback */); 713 mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); 714 } else if (mPowerPluggedIn || mEnableBatteryDefender) { 715 String indication = computePowerIndication(); 716 if (animate) { 717 mWakeLock.setAcquired(true); 718 mTopIndicationView.switchIndication(indication, null, true /* animate */, 719 () -> mWakeLock.setAcquired(false)); 720 } else { 721 mTopIndicationView.switchIndication(indication, null, false /* animate */, 722 null /* onAnimationEndCallback */); 723 } 724 } else { 725 String percentage = NumberFormat.getPercentInstance() 726 .format(mBatteryLevel / 100f); 727 mTopIndicationView.switchIndication(percentage, null /* indication */, 728 false /* animate */, null /* onAnimationEnd*/); 729 } 730 return; 731 } 732 733 // LOCK SCREEN 734 mTopIndicationView.setVisibility(GONE); 735 mTopIndicationView.setText(null); 736 mLockScreenIndicationView.setVisibility(View.VISIBLE); 737 updatePersistentIndications(animate, KeyguardUpdateMonitor.getCurrentUser()); 738 } 739 740 // animates textView - textView moves up and bounces down animateText(KeyguardIndicationTextView textView, String indication)741 private void animateText(KeyguardIndicationTextView textView, String indication) { 742 int yTranslation = mContext.getResources().getInteger( 743 R.integer.wired_charging_keyguard_text_animation_distance); 744 int animateUpDuration = mContext.getResources().getInteger( 745 R.integer.wired_charging_keyguard_text_animation_duration_up); 746 int animateDownDuration = mContext.getResources().getInteger( 747 R.integer.wired_charging_keyguard_text_animation_duration_down); 748 textView.animate().cancel(); 749 ViewClippingUtil.setClippingDeactivated(textView, true, mClippingParams); 750 textView.animate() 751 .translationYBy(yTranslation) 752 .setInterpolator(Interpolators.LINEAR) 753 .setDuration(animateUpDuration) 754 .setListener(new AnimatorListenerAdapter() { 755 private boolean mCancelled; 756 757 @Override 758 public void onAnimationStart(Animator animation) { 759 textView.switchIndication(indication, null); 760 } 761 762 @Override 763 public void onAnimationCancel(Animator animation) { 764 textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y); 765 mCancelled = true; 766 } 767 768 @Override 769 public void onAnimationEnd(Animator animation) { 770 if (mCancelled) { 771 ViewClippingUtil.setClippingDeactivated(textView, false, 772 mClippingParams); 773 return; 774 } 775 textView.animate() 776 .setDuration(animateDownDuration) 777 .setInterpolator(Interpolators.BOUNCE) 778 .translationY(BOUNCE_ANIMATION_FINAL_Y) 779 .setListener(new AnimatorListenerAdapter() { 780 @Override 781 public void onAnimationEnd(Animator animation) { 782 textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y); 783 ViewClippingUtil.setClippingDeactivated(textView, false, 784 mClippingParams); 785 // Unset the listener, otherwise this may persist for 786 // another view property animation 787 textView.animate().setListener(null); 788 } 789 }); 790 } 791 }); 792 } 793 computePowerIndication()794 protected String computePowerIndication() { 795 int chargingId; 796 if (mBatteryOverheated) { 797 chargingId = R.string.keyguard_plugged_in_charging_limited; 798 String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); 799 return mContext.getResources().getString(chargingId, percentage); 800 } else if (mPowerCharged) { 801 return mContext.getResources().getString(R.string.keyguard_charged); 802 } 803 804 final boolean hasChargingTime = mChargingTimeRemaining > 0; 805 if (mPowerPluggedInWired) { 806 switch (mChargingSpeed) { 807 case BatteryStatus.CHARGING_FAST: 808 chargingId = hasChargingTime 809 ? R.string.keyguard_indication_charging_time_fast 810 : R.string.keyguard_plugged_in_charging_fast; 811 break; 812 case BatteryStatus.CHARGING_SLOWLY: 813 chargingId = hasChargingTime 814 ? R.string.keyguard_indication_charging_time_slowly 815 : R.string.keyguard_plugged_in_charging_slowly; 816 break; 817 default: 818 chargingId = hasChargingTime 819 ? R.string.keyguard_indication_charging_time 820 : R.string.keyguard_plugged_in; 821 break; 822 } 823 } else if (mPowerPluggedInWireless) { 824 chargingId = hasChargingTime 825 ? R.string.keyguard_indication_charging_time_wireless 826 : R.string.keyguard_plugged_in_wireless; 827 } else { 828 chargingId = hasChargingTime 829 ? R.string.keyguard_indication_charging_time 830 : R.string.keyguard_plugged_in; 831 } 832 833 String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); 834 if (hasChargingTime) { 835 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( 836 mContext, mChargingTimeRemaining); 837 return mContext.getResources().getString(chargingId, chargingTimeFormatted, 838 percentage); 839 } else { 840 return mContext.getResources().getString(chargingId, percentage); 841 } 842 } 843 setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)844 public void setStatusBarKeyguardViewManager( 845 StatusBarKeyguardViewManager statusBarKeyguardViewManager) { 846 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 847 } 848 849 private final KeyguardUpdateMonitorCallback mTickReceiver = 850 new KeyguardUpdateMonitorCallback() { 851 @Override 852 public void onTimeChanged() { 853 if (mVisible) { 854 updateIndication(false /* animate */); 855 } 856 } 857 }; 858 859 private final Handler mHandler = new Handler() { 860 @Override 861 public void handleMessage(Message msg) { 862 if (msg.what == MSG_HIDE_TRANSIENT) { 863 hideTransientIndication(); 864 } else if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) { 865 showActionToUnlock(); 866 } else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) { 867 hideBiometricMessage(); 868 } 869 } 870 }; 871 872 /** 873 * Show message on the keyguard for how the user can unlock/enter their device. 874 */ showActionToUnlock()875 public void showActionToUnlock() { 876 if (mDozing 877 && !mKeyguardUpdateMonitor.getUserCanSkipBouncer( 878 KeyguardUpdateMonitor.getCurrentUser())) { 879 return; 880 } 881 882 if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 883 if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { 884 return; // udfps affordance is highlighted, no need to show action to unlock 885 } else if (mKeyguardUpdateMonitor.isFaceEnrolled()) { 886 String message = mContext.getString(R.string.keyguard_retry); 887 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState); 888 } 889 } else { 890 if (mKeyguardUpdateMonitor.isUdfpsSupported() 891 && mKeyguardUpdateMonitor.getUserCanSkipBouncer( 892 KeyguardUpdateMonitor.getCurrentUser())) { 893 showBiometricMessage(mContext.getString(R.string.keyguard_unlock_press)); 894 } else { 895 showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); 896 } 897 } 898 } 899 showTryFingerprintMsg(int msgId, String a11yString)900 private void showTryFingerprintMsg(int msgId, String a11yString) { 901 if (mKeyguardUpdateMonitor.isUdfpsSupported()) { 902 // if udfps available, there will always be a tappable affordance to unlock 903 // For example, the lock icon 904 if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) { 905 showBiometricMessage(R.string.keyguard_unlock_press); 906 } else if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) { 907 // since face is locked out, simply show "try fingerprint" 908 showBiometricMessage(R.string.keyguard_try_fingerprint); 909 } else { 910 showBiometricMessage(R.string.keyguard_face_failed_use_fp); 911 } 912 } else { 913 showBiometricMessage(R.string.keyguard_try_fingerprint); 914 } 915 916 // Although we suppress face auth errors visually, we still announce them for a11y 917 if (!TextUtils.isEmpty(a11yString)) { 918 mLockScreenIndicationView.announceForAccessibility(a11yString); 919 } 920 } 921 dump(FileDescriptor fd, PrintWriter pw, String[] args)922 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 923 pw.println("KeyguardIndicationController:"); 924 pw.println(" mInitialTextColorState: " + mInitialTextColorState); 925 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired); 926 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn); 927 pw.println(" mPowerCharged: " + mPowerCharged); 928 pw.println(" mChargingSpeed: " + mChargingSpeed); 929 pw.println(" mChargingWattage: " + mChargingWattage); 930 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn); 931 pw.println(" mDozing: " + mDozing); 932 pw.println(" mTransientIndication: " + mTransientIndication); 933 pw.println(" mBiometricMessage: " + mBiometricMessage); 934 pw.println(" mBatteryLevel: " + mBatteryLevel); 935 pw.println(" mBatteryPresent: " + mBatteryPresent); 936 pw.println(" mTextView.getText(): " + ( 937 mTopIndicationView == null ? null : mTopIndicationView.getText())); 938 pw.println(" computePowerIndication(): " + computePowerIndication()); 939 mRotateTextViewController.dump(fd, pw, args); 940 } 941 942 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { 943 public static final int HIDE_DELAY_MS = 5000; 944 945 @Override onRefreshBatteryInfo(BatteryStatus status)946 public void onRefreshBatteryInfo(BatteryStatus status) { 947 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING 948 || status.isCharged(); 949 boolean wasPluggedIn = mPowerPluggedIn; 950 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull; 951 mPowerPluggedInWireless = status.isPluggedInWireless() && isChargingOrFull; 952 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; 953 mPowerCharged = status.isCharged(); 954 mChargingWattage = status.maxChargingWattage; 955 mChargingSpeed = status.getChargingSpeed(mContext); 956 mBatteryLevel = status.level; 957 mBatteryPresent = status.present; 958 mBatteryOverheated = status.isOverheated(); 959 mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn(); 960 try { 961 mChargingTimeRemaining = mPowerPluggedIn 962 ? mBatteryInfo.computeChargeTimeRemaining() : -1; 963 } catch (RemoteException e) { 964 Log.e(TAG, "Error calling IBatteryStats: ", e); 965 mChargingTimeRemaining = -1; 966 } 967 updateIndication(!wasPluggedIn && mPowerPluggedInWired); 968 if (mDozing) { 969 if (!wasPluggedIn && mPowerPluggedIn) { 970 showTransientIndication(computePowerIndication()); 971 } else if (wasPluggedIn && !mPowerPluggedIn) { 972 hideTransientIndication(); 973 } 974 } 975 } 976 977 @Override onBiometricHelp(int msgId, String helpString, BiometricSourceType biometricSourceType)978 public void onBiometricHelp(int msgId, String helpString, 979 BiometricSourceType biometricSourceType) { 980 // TODO(b/141025588): refactor to reduce repetition of code/comments 981 // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong 982 // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to 983 // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the 984 // check of whether non-strong biometric is allowed 985 if (!mKeyguardUpdateMonitor 986 .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) { 987 return; 988 } 989 boolean showActionToUnlock = 990 msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; 991 if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 992 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, 993 mInitialTextColorState); 994 } else if (mKeyguardUpdateMonitor.isScreenOn()) { 995 if (biometricSourceType == BiometricSourceType.FACE 996 && shouldSuppressFaceMsgAndShowTryFingerprintMsg()) { 997 showTryFingerprintMsg(msgId, helpString); 998 return; 999 } 1000 showBiometricMessage(helpString); 1001 } else if (showActionToUnlock) { 1002 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK), 1003 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); 1004 } 1005 } 1006 1007 @Override onBiometricError(int msgId, String errString, BiometricSourceType biometricSourceType)1008 public void onBiometricError(int msgId, String errString, 1009 BiometricSourceType biometricSourceType) { 1010 if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) { 1011 return; 1012 } 1013 if (biometricSourceType == BiometricSourceType.FACE 1014 && shouldSuppressFaceMsgAndShowTryFingerprintMsg() 1015 && !mStatusBarKeyguardViewManager.isBouncerShowing() 1016 && mKeyguardUpdateMonitor.isScreenOn()) { 1017 showTryFingerprintMsg(msgId, errString); 1018 return; 1019 } 1020 if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { 1021 // The face timeout message is not very actionable, let's ask the user to 1022 // manually retry. 1023 if (!mStatusBarKeyguardViewManager.isBouncerShowing() 1024 && mKeyguardUpdateMonitor.isUdfpsEnrolled() 1025 && mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) { 1026 showTryFingerprintMsg(msgId, errString); 1027 } else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { 1028 mStatusBarKeyguardViewManager.showBouncerMessage( 1029 mContext.getResources().getString(R.string.keyguard_unlock_press), 1030 mInitialTextColorState 1031 ); 1032 } else { 1033 // suggest swiping up to unlock (try face auth again or swipe up to bouncer) 1034 showActionToUnlock(); 1035 } 1036 } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 1037 mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState); 1038 } else if (mKeyguardUpdateMonitor.isScreenOn()) { 1039 showBiometricMessage(errString); 1040 } else { 1041 mMessageToShowOnScreenOn = errString; 1042 } 1043 } 1044 shouldSuppressBiometricError(int msgId, BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor)1045 private boolean shouldSuppressBiometricError(int msgId, 1046 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) { 1047 if (biometricSourceType == BiometricSourceType.FINGERPRINT) 1048 return shouldSuppressFingerprintError(msgId, updateMonitor); 1049 if (biometricSourceType == BiometricSourceType.FACE) 1050 return shouldSuppressFaceError(msgId, updateMonitor); 1051 return false; 1052 } 1053 shouldSuppressFingerprintError(int msgId, KeyguardUpdateMonitor updateMonitor)1054 private boolean shouldSuppressFingerprintError(int msgId, 1055 KeyguardUpdateMonitor updateMonitor) { 1056 // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong 1057 // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to 1058 // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the 1059 // check of whether non-strong biometric is allowed 1060 return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) 1061 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) 1062 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED 1063 || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED); 1064 } 1065 shouldSuppressFaceMsgAndShowTryFingerprintMsg()1066 private boolean shouldSuppressFaceMsgAndShowTryFingerprintMsg() { 1067 // For dual biometric, don't show face auth messages 1068 return mKeyguardUpdateMonitor.isFingerprintDetectionRunning() 1069 && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( 1070 true /* isStrongBiometric */); 1071 } 1072 shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor)1073 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) { 1074 // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong 1075 // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to 1076 // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the 1077 // check of whether non-strong biometric is allowed 1078 return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */) 1079 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) 1080 || msgId == FaceManager.FACE_ERROR_CANCELED); 1081 } 1082 1083 @Override onTrustAgentErrorMessage(CharSequence message)1084 public void onTrustAgentErrorMessage(CharSequence message) { 1085 showBiometricMessage(message); 1086 } 1087 1088 @Override onScreenTurnedOn()1089 public void onScreenTurnedOn() { 1090 if (mMessageToShowOnScreenOn != null) { 1091 showBiometricMessage(mMessageToShowOnScreenOn); 1092 // We want to keep this message around in case the screen was off 1093 hideBiometricMessageDelayed(HIDE_DELAY_MS); 1094 mMessageToShowOnScreenOn = null; 1095 } 1096 } 1097 1098 @Override onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType)1099 public void onBiometricRunningStateChanged(boolean running, 1100 BiometricSourceType biometricSourceType) { 1101 if (running && biometricSourceType == BiometricSourceType.FACE) { 1102 // Let's hide any previous messages when authentication starts, otherwise 1103 // multiple auth attempts would overlap. 1104 hideBiometricMessage(); 1105 mMessageToShowOnScreenOn = null; 1106 } 1107 } 1108 1109 @Override onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, boolean isStrongBiometric)1110 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, 1111 boolean isStrongBiometric) { 1112 super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric); 1113 hideBiometricMessage(); 1114 1115 if (biometricSourceType == BiometricSourceType.FACE 1116 && !mKeyguardBypassController.canBypass()) { 1117 showActionToUnlock(); 1118 } 1119 } 1120 1121 @Override onUserSwitchComplete(int userId)1122 public void onUserSwitchComplete(int userId) { 1123 if (mVisible) { 1124 updateIndication(false); 1125 } 1126 } 1127 1128 @Override onUserUnlocked()1129 public void onUserUnlocked() { 1130 if (mVisible) { 1131 updateIndication(false); 1132 } 1133 } 1134 1135 @Override onLogoutEnabledChanged()1136 public void onLogoutEnabledChanged() { 1137 if (mVisible) { 1138 updateIndication(false); 1139 } 1140 } 1141 1142 @Override onRequireUnlockForNfc()1143 public void onRequireUnlockForNfc() { 1144 showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc)); 1145 hideTransientIndicationDelayed(HIDE_DELAY_MS); 1146 } 1147 } 1148 1149 private StatusBarStateController.StateListener mStatusBarStateListener = 1150 new StatusBarStateController.StateListener() { 1151 @Override 1152 public void onStateChanged(int newState) { 1153 setVisible(newState == StatusBarState.KEYGUARD); 1154 } 1155 1156 @Override 1157 public void onDozingChanged(boolean dozing) { 1158 if (mDozing == dozing) { 1159 return; 1160 } 1161 mDozing = dozing; 1162 1163 if (mDozing) { 1164 hideBiometricMessage(); 1165 } 1166 updateIndication(false); 1167 } 1168 }; 1169 1170 private KeyguardStateController.Callback mKeyguardStateCallback = 1171 new KeyguardStateController.Callback() { 1172 @Override 1173 public void onUnlockedChanged() { 1174 updateIndication(false); 1175 } 1176 1177 @Override 1178 public void onKeyguardShowingChanged() { 1179 if (!mKeyguardStateController.isShowing()) { 1180 mTopIndicationView.clearMessages(); 1181 mRotateTextViewController.clearMessages(); 1182 } 1183 } 1184 }; 1185 } 1186