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