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