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