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