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