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