1/*
2 * Copyright (C) 2010 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
17package com.android.systemui.statusbar.phone;
18
19import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
20import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
21import static android.app.StatusBarManager.windowStateToString;
22
23import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
24import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
25import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
26import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
31import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
32import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
33import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
34
35import android.animation.Animator;
36import android.animation.AnimatorListenerAdapter;
37import android.annotation.NonNull;
38import android.annotation.Nullable;
39import android.app.ActivityManager;
40import android.app.ActivityManager.StackId;
41import android.app.ActivityOptions;
42import android.app.INotificationManager;
43import android.app.KeyguardManager;
44import android.app.Notification;
45import android.app.NotificationChannel;
46import android.app.NotificationManager;
47import android.app.PendingIntent;
48import android.app.RemoteInput;
49import android.app.StatusBarManager;
50import android.app.TaskStackBuilder;
51import android.app.WallpaperColors;
52import android.app.WallpaperManager;
53import android.app.admin.DevicePolicyManager;
54import android.content.BroadcastReceiver;
55import android.content.ComponentCallbacks2;
56import android.content.ComponentName;
57import android.content.Context;
58import android.content.Intent;
59import android.content.IntentFilter;
60import android.content.IntentSender;
61import android.content.om.IOverlayManager;
62import android.content.om.OverlayInfo;
63import android.content.pm.ApplicationInfo;
64import android.content.pm.IPackageManager;
65import android.content.pm.PackageManager;
66import android.content.pm.PackageManager.NameNotFoundException;
67import android.content.pm.UserInfo;
68import android.content.res.Configuration;
69import android.content.res.Resources;
70import android.database.ContentObserver;
71import android.graphics.Bitmap;
72import android.graphics.Canvas;
73import android.graphics.ColorFilter;
74import android.graphics.PixelFormat;
75import android.graphics.Point;
76import android.graphics.PointF;
77import android.graphics.PorterDuff;
78import android.graphics.PorterDuffXfermode;
79import android.graphics.Rect;
80import android.graphics.drawable.BitmapDrawable;
81import android.graphics.drawable.ColorDrawable;
82import android.graphics.drawable.Drawable;
83import android.media.AudioAttributes;
84import android.media.MediaMetadata;
85import android.media.session.MediaController;
86import android.media.session.MediaSession;
87import android.media.session.MediaSessionManager;
88import android.media.session.PlaybackState;
89import android.metrics.LogMaker;
90import android.net.Uri;
91import android.os.AsyncTask;
92import android.os.Build;
93import android.os.Bundle;
94import android.os.Handler;
95import android.os.IBinder;
96import android.os.Message;
97import android.os.PowerManager;
98import android.os.RemoteException;
99import android.os.ServiceManager;
100import android.os.SystemClock;
101import android.os.SystemProperties;
102import android.os.Trace;
103import android.os.UserHandle;
104import android.os.UserManager;
105import android.os.Vibrator;
106import android.provider.Settings;
107import android.service.notification.NotificationListenerService.RankingMap;
108import android.service.notification.StatusBarNotification;
109import android.service.vr.IVrManager;
110import android.service.vr.IVrStateCallbacks;
111import android.text.TextUtils;
112import android.util.ArraySet;
113import android.util.DisplayMetrics;
114import android.util.EventLog;
115import android.util.Log;
116import android.util.Slog;
117import android.util.SparseArray;
118import android.util.SparseBooleanArray;
119import android.view.Display;
120import android.view.HapticFeedbackConstants;
121import android.view.IWindowManager;
122import android.view.KeyEvent;
123import android.view.LayoutInflater;
124import android.view.MotionEvent;
125import android.view.ThreadedRenderer;
126import android.view.View;
127import android.view.ViewAnimationUtils;
128import android.view.ViewGroup;
129import android.view.ViewParent;
130import android.view.ViewStub;
131import android.view.ViewTreeObserver;
132import android.view.WindowManager;
133import android.view.WindowManagerGlobal;
134import android.view.accessibility.AccessibilityManager;
135import android.view.animation.AccelerateInterpolator;
136import android.view.animation.Interpolator;
137import android.widget.DateTimeView;
138import android.widget.ImageView;
139import android.widget.RemoteViews;
140import android.widget.TextView;
141import android.widget.Toast;
142
143import com.android.internal.annotations.VisibleForTesting;
144import com.android.internal.colorextraction.ColorExtractor;
145import com.android.internal.logging.MetricsLogger;
146import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
147import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
148import com.android.internal.statusbar.IStatusBarService;
149import com.android.internal.statusbar.NotificationVisibility;
150import com.android.internal.statusbar.StatusBarIcon;
151import com.android.internal.util.NotificationMessagingUtil;
152import com.android.internal.widget.LockPatternUtils;
153import com.android.keyguard.KeyguardHostView.OnDismissAction;
154import com.android.keyguard.KeyguardUpdateMonitor;
155import com.android.keyguard.KeyguardUpdateMonitorCallback;
156import com.android.keyguard.ViewMediatorCallback;
157import com.android.systemui.ActivityStarterDelegate;
158import com.android.systemui.AutoReinflateContainer;
159import com.android.systemui.DejankUtils;
160import com.android.systemui.DemoMode;
161import com.android.systemui.Dependency;
162import com.android.systemui.EventLogTags;
163import com.android.systemui.ForegroundServiceController;
164import com.android.systemui.Interpolators;
165import com.android.systemui.Prefs;
166import com.android.systemui.R;
167import com.android.systemui.RecentsComponent;
168import com.android.systemui.SwipeHelper;
169import com.android.systemui.SystemUI;
170import com.android.systemui.SystemUIFactory;
171import com.android.systemui.UiOffloadThread;
172import com.android.systemui.assist.AssistManager;
173import com.android.systemui.classifier.FalsingLog;
174import com.android.systemui.classifier.FalsingManager;
175import com.android.systemui.colorextraction.SysuiColorExtractor;
176import com.android.systemui.doze.DozeHost;
177import com.android.systemui.doze.DozeLog;
178import com.android.systemui.doze.DozeReceiver;
179import com.android.systemui.fragments.ExtensionFragmentListener;
180import com.android.systemui.fragments.FragmentHostManager;
181import com.android.systemui.keyguard.KeyguardViewMediator;
182import com.android.systemui.keyguard.ScreenLifecycle;
183import com.android.systemui.keyguard.WakefulnessLifecycle;
184import com.android.systemui.plugins.ActivityStarter;
185import com.android.systemui.plugins.qs.QS;
186import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
187import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
188import com.android.systemui.qs.QSFragment;
189import com.android.systemui.qs.QSPanel;
190import com.android.systemui.qs.QSTileHost;
191import com.android.systemui.qs.car.CarQSFragment;
192import com.android.systemui.recents.Recents;
193import com.android.systemui.recents.ScreenPinningRequest;
194import com.android.systemui.recents.events.EventBus;
195import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
196import com.android.systemui.recents.events.activity.UndockingTaskEvent;
197import com.android.systemui.recents.misc.SystemServicesProxy;
198import com.android.systemui.stackdivider.Divider;
199import com.android.systemui.stackdivider.WindowManagerProxy;
200import com.android.systemui.statusbar.ActivatableNotificationView;
201import com.android.systemui.statusbar.BackDropView;
202import com.android.systemui.statusbar.CommandQueue;
203import com.android.systemui.statusbar.DismissView;
204import com.android.systemui.statusbar.DragDownHelper;
205import com.android.systemui.statusbar.EmptyShadeView;
206import com.android.systemui.statusbar.ExpandableNotificationRow;
207import com.android.systemui.statusbar.GestureRecorder;
208import com.android.systemui.statusbar.KeyboardShortcuts;
209import com.android.systemui.statusbar.KeyguardIndicationController;
210import com.android.systemui.statusbar.NotificationData;
211import com.android.systemui.statusbar.NotificationData.Entry;
212import com.android.systemui.statusbar.NotificationGuts;
213import com.android.systemui.statusbar.NotificationInfo;
214import com.android.systemui.statusbar.NotificationShelf;
215import com.android.systemui.statusbar.NotificationSnooze;
216import com.android.systemui.statusbar.RemoteInputController;
217import com.android.systemui.statusbar.ScrimView;
218import com.android.systemui.statusbar.SignalClusterView;
219import com.android.systemui.statusbar.StatusBarState;
220import com.android.systemui.statusbar.notification.AboveShelfObserver;
221import com.android.systemui.statusbar.notification.InflationException;
222import com.android.systemui.statusbar.notification.RowInflaterTask;
223import com.android.systemui.statusbar.notification.VisualStabilityManager;
224import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
225import com.android.systemui.statusbar.policy.BatteryController;
226import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
227import com.android.systemui.statusbar.policy.BrightnessMirrorController;
228import com.android.systemui.statusbar.policy.ConfigurationController;
229import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
230import com.android.systemui.statusbar.policy.DarkIconDispatcher;
231import com.android.systemui.statusbar.policy.DeviceProvisionedController;
232import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
233import com.android.systemui.statusbar.policy.ExtensionController;
234import com.android.systemui.statusbar.policy.HeadsUpManager;
235import com.android.systemui.statusbar.policy.KeyguardMonitor;
236import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
237import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
238import com.android.systemui.statusbar.policy.NetworkController;
239import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
240import com.android.systemui.statusbar.policy.PreviewInflater;
241import com.android.systemui.statusbar.policy.RemoteInputView;
242import com.android.systemui.statusbar.policy.UserInfoController;
243import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
244import com.android.systemui.statusbar.policy.UserSwitcherController;
245import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
246import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
247        .OnChildLocationsChangedListener;
248import com.android.systemui.statusbar.stack.StackStateAnimator;
249import com.android.systemui.util.NotificationChannels;
250import com.android.systemui.util.leak.LeakDetector;
251import com.android.systemui.volume.VolumeComponent;
252
253import java.io.FileDescriptor;
254import java.io.PrintWriter;
255import java.io.StringWriter;
256import java.util.ArrayList;
257import java.util.Collection;
258import java.util.Collections;
259import java.util.HashMap;
260import java.util.HashSet;
261import java.util.List;
262import java.util.Locale;
263import java.util.Map;
264import java.util.Set;
265import java.util.Stack;
266
267public class StatusBar extends SystemUI implements DemoMode,
268        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
269        OnHeadsUpChangedListener, VisualStabilityManager.Callback, CommandQueue.Callbacks,
270        ActivatableNotificationView.OnActivatedListener,
271        ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
272        ExpandableNotificationRow.OnExpandClickListener, InflationCallback,
273        ColorExtractor.OnColorsChangedListener, ConfigurationListener {
274    public static final boolean MULTIUSER_DEBUG = false;
275
276    public static final boolean ENABLE_REMOTE_INPUT =
277            SystemProperties.getBoolean("debug.enable_remote_input", true);
278    public static final boolean ENABLE_CHILD_NOTIFICATIONS
279            = SystemProperties.getBoolean("debug.child_notifs", true);
280    public static final boolean FORCE_REMOTE_INPUT_HISTORY =
281            SystemProperties.getBoolean("debug.force_remoteinput_history", false);
282    private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
283
284    protected static final int MSG_SHOW_RECENT_APPS = 1019;
285    protected static final int MSG_HIDE_RECENT_APPS = 1020;
286    protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
287    protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
288    protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
289    protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
290    protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
291
292    protected static final boolean ENABLE_HEADS_UP = true;
293    protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
294
295    // Must match constant in Settings. Used to highlight preferences when linking to Settings.
296    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
297
298    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
299
300    // Should match the values in PhoneWindowManager
301    public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
302    public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
303    static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
304
305    private static final String BANNER_ACTION_CANCEL =
306            "com.android.systemui.statusbar.banner_action_cancel";
307    private static final String BANNER_ACTION_SETUP =
308            "com.android.systemui.statusbar.banner_action_setup";
309    private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
310            = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
311    public static final String TAG = "StatusBar";
312    public static final boolean DEBUG = false;
313    public static final boolean SPEW = false;
314    public static final boolean DUMPTRUCK = true; // extra dumpsys info
315    public static final boolean DEBUG_GESTURES = false;
316    public static final boolean DEBUG_MEDIA = false;
317    public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
318    public static final boolean DEBUG_CAMERA_LIFT = false;
319
320    public static final boolean DEBUG_WINDOW_STATE = false;
321
322    // additional instrumentation for testing purposes; intended to be left on during development
323    public static final boolean CHATTY = DEBUG;
324
325    public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
326
327    public static final String ACTION_FAKE_ARTWORK = "fake_artwork";
328
329    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
330    private static final int MSG_CLOSE_PANELS = 1001;
331    private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
332    private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
333    // 1020-1040 reserved for BaseStatusBar
334
335    // Time after we abort the launch transition.
336    private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
337
338    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
339
340    private static final int STATUS_OR_NAV_TRANSIENT =
341            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
342    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
343
344    /** The minimum delay in ms between reports of notification visibility. */
345    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
346
347    /**
348     * The delay to reset the hint text when the hint animation is finished running.
349     */
350    private static final int HINT_RESET_DELAY_MS = 1200;
351
352    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
353            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
354            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
355            .build();
356
357    public static final int FADE_KEYGUARD_START_DELAY = 100;
358    public static final int FADE_KEYGUARD_DURATION = 300;
359    public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
360
361    /** If true, the system is in the half-boot-to-decryption-screen state.
362     * Prudently disable QS and notifications.  */
363    private static final boolean ONLY_CORE_APPS;
364
365    /** If true, the lockscreen will show a distinct wallpaper */
366    private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
367
368    /* If true, the device supports freeform window management.
369     * This affects the status bar UI. */
370    private static final boolean FREEFORM_WINDOW_MANAGEMENT;
371
372    /**
373     * How long to wait before auto-dismissing a notification that was kept for remote input, and
374     * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
375     * these given that they technically don't exist anymore. We wait a bit in case the app issues
376     * an update.
377     */
378    private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
379
380    /**
381     * Never let the alpha become zero for surfaces that draw with SRC - otherwise the RenderNode
382     * won't draw anything and uninitialized memory will show through
383     * if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
384     * libhwui.
385     */
386    private static final float SRC_MIN_ALPHA = 0.002f;
387
388    static {
389        boolean onlyCoreApps;
390        boolean freeformWindowManagement;
391        try {
392            IPackageManager packageManager =
393                    IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
394            onlyCoreApps = packageManager.isOnlyCoreApps();
395            freeformWindowManagement = packageManager.hasSystemFeature(
396                    PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, 0);
397        } catch (RemoteException e) {
398            onlyCoreApps = false;
399            freeformWindowManagement = false;
400        }
401        ONLY_CORE_APPS = onlyCoreApps;
402        FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement;
403    }
404
405    /**
406     * The {@link StatusBarState} of the status bar.
407     */
408    protected int mState;
409    protected boolean mBouncerShowing;
410    protected boolean mShowLockscreenNotifications;
411    protected boolean mAllowLockscreenRemoteInput;
412
413    PhoneStatusBarPolicy mIconPolicy;
414
415    VolumeComponent mVolumeComponent;
416    BrightnessMirrorController mBrightnessMirrorController;
417    protected FingerprintUnlockController mFingerprintUnlockController;
418    LightBarController mLightBarController;
419    protected LockscreenWallpaper mLockscreenWallpaper;
420
421    int mNaturalBarHeight = -1;
422
423    Point mCurrentDisplaySize = new Point();
424
425    protected StatusBarWindowView mStatusBarWindow;
426    protected PhoneStatusBarView mStatusBarView;
427    private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
428    protected StatusBarWindowManager mStatusBarWindowManager;
429    protected UnlockMethodCache mUnlockMethodCache;
430    private DozeServiceHost mDozeServiceHost = new DozeServiceHost();
431    private boolean mWakeUpComingFromTouch;
432    private PointF mWakeUpTouchLocation;
433
434    int mPixelFormat;
435    Object mQueueLock = new Object();
436
437    protected StatusBarIconController mIconController;
438
439    // expanded notifications
440    protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
441    View mExpandedContents;
442    TextView mNotificationPanelDebugText;
443
444    /**
445     * {@code true} if notifications not part of a group should by default be rendered in their
446     * expanded state. If {@code false}, then only the first notification will be expanded if
447     * possible.
448     */
449    private boolean mAlwaysExpandNonGroupedNotification;
450
451    // settings
452    private QSPanel mQSPanel;
453
454    // top bar
455    protected KeyguardStatusBarView mKeyguardStatusBar;
456    boolean mLeaveOpenOnKeyguardHide;
457    KeyguardIndicationController mKeyguardIndicationController;
458
459    // Keyguard is going away soon.
460    private boolean mKeyguardGoingAway;
461    // Keyguard is actually fading away now.
462    protected boolean mKeyguardFadingAway;
463    protected long mKeyguardFadingAwayDelay;
464    protected long mKeyguardFadingAwayDuration;
465
466    // RemoteInputView to be activated after unlock
467    private View mPendingRemoteInputView;
468    private View mPendingWorkRemoteInputView;
469
470    private View mReportRejectedTouch;
471
472    int mMaxAllowedKeyguardNotifications;
473
474    boolean mExpandedVisible;
475
476    // the tracker view
477    int mTrackingPosition; // the position of the top of the tracking view.
478
479    // Tracking finger for opening/closing.
480    boolean mTracking;
481
482    int[] mAbsPos = new int[2];
483    ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
484
485    // for disabling the status bar
486    int mDisabled1 = 0;
487    int mDisabled2 = 0;
488
489    // tracking calls to View.setSystemUiVisibility()
490    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
491    private final Rect mLastFullscreenStackBounds = new Rect();
492    private final Rect mLastDockedStackBounds = new Rect();
493    private final Rect mTmpRect = new Rect();
494
495    // last value sent to window manager
496    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
497
498    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
499
500    // XXX: gesture research
501    private final GestureRecorder mGestureRec = DEBUG_GESTURES
502        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
503        : null;
504
505    private ScreenPinningRequest mScreenPinningRequest;
506
507    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
508
509    // ensure quick settings is disabled until the current user makes it through the setup wizard
510    private boolean mUserSetup = false;
511    private DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
512        @Override
513        public void onUserSetupChanged() {
514            final boolean userSetup = mDeviceProvisionedController.isUserSetup(
515                    mDeviceProvisionedController.getCurrentUser());
516            if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
517                    "userSetup=%s mUserSetup=%s", userSetup, mUserSetup));
518
519            if (userSetup != mUserSetup) {
520                mUserSetup = userSetup;
521                if (!mUserSetup && mStatusBarView != null)
522                    animateCollapseQuickSettings();
523                if (mNotificationPanel != null) {
524                    mNotificationPanel.setUserSetupComplete(mUserSetup);
525                }
526                updateQsExpansionEnabled();
527            }
528        }
529    };
530
531    protected H mHandler = createHandler();
532    final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
533        @Override
534        public void onChange(boolean selfChange) {
535            boolean wasUsing = mUseHeadsUp;
536            mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
537                    && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
538                    mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
539                    Settings.Global.HEADS_UP_OFF);
540            mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
541                    mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
542            Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
543            if (wasUsing != mUseHeadsUp) {
544                if (!mUseHeadsUp) {
545                    Log.d(TAG, "dismissing any existing heads up notification on disable event");
546                    mHeadsUpManager.releaseAllImmediately();
547                }
548            }
549        }
550    };
551
552    private int mInteractingWindows;
553    private boolean mAutohideSuspended;
554    private int mStatusBarMode;
555    private int mMaxKeyguardNotifications;
556
557    private ViewMediatorCallback mKeyguardViewMediatorCallback;
558    protected ScrimController mScrimController;
559    protected DozeScrimController mDozeScrimController;
560    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
561
562    private final Runnable mAutohide = () -> {
563        int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
564        if (mSystemUiVisibility != requested) {
565            notifyUiVisibilityChanged(requested);
566        }
567    };
568
569    private boolean mWaitingForKeyguardExit;
570    protected boolean mDozing;
571    private boolean mDozingRequested;
572    protected boolean mScrimSrcModeEnabled;
573
574    public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN;
575    public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT;
576
577    protected BackDropView mBackdrop;
578    protected ImageView mBackdropFront, mBackdropBack;
579    protected PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
580    protected PorterDuffXfermode mSrcOverXferMode =
581            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
582
583    private MediaSessionManager mMediaSessionManager;
584    private MediaController mMediaController;
585    private String mMediaNotificationKey;
586    private MediaMetadata mMediaMetadata;
587    private MediaController.Callback mMediaListener
588            = new MediaController.Callback() {
589        @Override
590        public void onPlaybackStateChanged(PlaybackState state) {
591            super.onPlaybackStateChanged(state);
592            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
593            if (state != null) {
594                if (!isPlaybackActive(state.getState())) {
595                    clearCurrentMediaNotification();
596                    updateMediaMetaData(true, true);
597                }
598            }
599        }
600
601        @Override
602        public void onMetadataChanged(MediaMetadata metadata) {
603            super.onMetadataChanged(metadata);
604            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
605            mMediaMetadata = metadata;
606            updateMediaMetaData(true, true);
607        }
608    };
609
610    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
611            new OnChildLocationsChangedListener() {
612        @Override
613        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
614            userActivity();
615        }
616    };
617
618    private int mDisabledUnmodified1;
619    private int mDisabledUnmodified2;
620
621    /** Keys of notifications currently visible to the user. */
622    private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
623            new ArraySet<>();
624    private long mLastVisibilityReportUptimeMs;
625
626    private Runnable mLaunchTransitionEndRunnable;
627    protected boolean mLaunchTransitionFadingAway;
628    private ExpandableNotificationRow mDraggedDownRow;
629    private boolean mLaunchCameraOnScreenTurningOn;
630    private boolean mLaunchCameraOnFinishedGoingToSleep;
631    private int mLastCameraLaunchSource;
632    private PowerManager.WakeLock mGestureWakeLock;
633    private Vibrator mVibrator;
634    private long[] mCameraLaunchGestureVibePattern;
635
636    private final int[] mTmpInt2 = new int[2];
637
638    // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
639    private int mLastLoggedStateFingerprint;
640    private boolean mTopHidesStatusBar;
641    private boolean mStatusBarWindowHidden;
642    private boolean mHideIconsForBouncer;
643    private boolean mIsOccluded;
644    private boolean mWereIconsJustHidden;
645    private boolean mBouncerWasShowingWhenHidden;
646
647    public boolean isStartedGoingToSleep() {
648        return mStartedGoingToSleep;
649    }
650
651    /**
652     * If set, the device has started going to sleep but isn't fully non-interactive yet.
653     */
654    protected boolean mStartedGoingToSleep;
655
656    private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
657            new OnChildLocationsChangedListener() {
658                @Override
659                public void onChildLocationsChanged(
660                        NotificationStackScrollLayout stackScrollLayout) {
661                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
662                        // Visibilities will be reported when the existing
663                        // callback is executed.
664                        return;
665                    }
666                    // Calculate when we're allowed to run the visibility
667                    // reporter. Note that this timestamp might already have
668                    // passed. That's OK, the callback will just be executed
669                    // ASAP.
670                    long nextReportUptimeMs =
671                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
672                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
673                }
674            };
675
676    // Tracks notifications currently visible in mNotificationStackScroller and
677    // emits visibility events via NoMan on changes.
678    protected final Runnable mVisibilityReporter = new Runnable() {
679        private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications =
680                new ArraySet<>();
681        private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications =
682                new ArraySet<>();
683        private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications =
684                new ArraySet<>();
685
686        @Override
687        public void run() {
688            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
689            final String mediaKey = getCurrentMediaNotificationKey();
690
691            // 1. Loop over mNotificationData entries:
692            //   A. Keep list of visible notifications.
693            //   B. Keep list of previously hidden, now visible notifications.
694            // 2. Compute no-longer visible notifications by removing currently
695            //    visible notifications from the set of previously visible
696            //    notifications.
697            // 3. Report newly visible and no-longer visible notifications.
698            // 4. Keep currently visible notifications for next report.
699            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
700            int N = activeNotifications.size();
701            for (int i = 0; i < N; i++) {
702                Entry entry = activeNotifications.get(i);
703                String key = entry.notification.getKey();
704                boolean isVisible = mStackScroller.isInVisibleLocation(entry.row);
705                NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible);
706                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
707                if (isVisible) {
708                    // Build new set of visible notifications.
709                    mTmpCurrentlyVisibleNotifications.add(visObj);
710                    if (!previouslyVisible) {
711                        mTmpNewlyVisibleNotifications.add(visObj);
712                    }
713                } else {
714                    // release object
715                    visObj.recycle();
716                }
717            }
718            mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications);
719            mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
720
721            logNotificationVisibilityChanges(
722                    mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications);
723
724            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
725            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
726
727            recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
728            mTmpCurrentlyVisibleNotifications.clear();
729            mTmpNewlyVisibleNotifications.clear();
730            mTmpNoLongerVisibleNotifications.clear();
731        }
732    };
733
734    private NotificationMessagingUtil mMessagingUtil;
735    private KeyguardUserSwitcher mKeyguardUserSwitcher;
736    private UserSwitcherController mUserSwitcherController;
737    private NetworkController mNetworkController;
738    private KeyguardMonitorImpl mKeyguardMonitor
739            = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
740    private BatteryController mBatteryController;
741    protected boolean mPanelExpanded;
742    private IOverlayManager mOverlayManager;
743    private boolean mKeyguardRequested;
744    private boolean mIsKeyguard;
745    private LogMaker mStatusBarStateLog;
746    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
747    protected NotificationIconAreaController mNotificationIconAreaController;
748    private boolean mReinflateNotificationsOnUserSwitched;
749    private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
750    private boolean mClearAllEnabled;
751    @Nullable private View mAmbientIndicationContainer;
752    private String mKeyToRemoveOnGutsClosed;
753    private SysuiColorExtractor mColorExtractor;
754    private ForegroundServiceController mForegroundServiceController;
755    private ScreenLifecycle mScreenLifecycle;
756    @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
757
758    private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
759        final int N = array.size();
760        for (int i = 0 ; i < N; i++) {
761            array.valueAt(i).recycle();
762        }
763        array.clear();
764    }
765
766    private final View.OnClickListener mGoToLockedShadeListener = v -> {
767        if (mState == StatusBarState.KEYGUARD) {
768            wakeUpIfDozing(SystemClock.uptimeMillis(), v);
769            goToLockedShade(null);
770        }
771    };
772    private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
773            = new HashMap<>();
774    private RankingMap mLatestRankingMap;
775    private boolean mNoAnimationOnNextBarModeChange;
776    private FalsingManager mFalsingManager;
777
778    private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
779        @Override
780        public void onDreamingStateChanged(boolean dreaming) {
781            if (dreaming) {
782                maybeEscalateHeadsUp();
783            }
784        }
785    };
786
787    private NavigationBarFragment mNavigationBar;
788    private View mNavigationBarView;
789
790    @Override
791    public void start() {
792        mNetworkController = Dependency.get(NetworkController.class);
793        mUserSwitcherController = Dependency.get(UserSwitcherController.class);
794        mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
795        mScreenLifecycle.addObserver(mScreenObserver);
796        mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
797        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
798        mBatteryController = Dependency.get(BatteryController.class);
799        mAssistManager = Dependency.get(AssistManager.class);
800        mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
801        mOverlayManager = IOverlayManager.Stub.asInterface(
802                ServiceManager.getService(Context.OVERLAY_SERVICE));
803
804        mColorExtractor = Dependency.get(SysuiColorExtractor.class);
805        mColorExtractor.addOnColorsChangedListener(this);
806
807        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
808
809        mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
810
811        mDisplay = mWindowManager.getDefaultDisplay();
812        updateDisplaySize();
813
814        Resources res = mContext.getResources();
815        mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
816        mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
817        mAlwaysExpandNonGroupedNotification =
818                res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
819
820        DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
821        putComponent(StatusBar.class, this);
822
823        // start old BaseStatusBar.start().
824        mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
825        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
826                Context.DEVICE_POLICY_SERVICE);
827
828        mNotificationData = new NotificationData(this);
829        mMessagingUtil = new NotificationMessagingUtil(mContext);
830
831        mAccessibilityManager = (AccessibilityManager)
832                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
833
834        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
835
836        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
837        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
838        mContext.getContentResolver().registerContentObserver(
839                Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
840                mSettingsObserver);
841        mContext.getContentResolver().registerContentObserver(
842                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
843                mLockscreenSettingsObserver,
844                UserHandle.USER_ALL);
845        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
846            mContext.getContentResolver().registerContentObserver(
847                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
848                    false,
849                    mSettingsObserver,
850                    UserHandle.USER_ALL);
851        }
852
853        mContext.getContentResolver().registerContentObserver(
854                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
855                true,
856                mLockscreenSettingsObserver,
857                UserHandle.USER_ALL);
858
859        mBarService = IStatusBarService.Stub.asInterface(
860                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
861
862        mRecents = getComponent(Recents.class);
863
864        final Configuration currentConfig = res.getConfiguration();
865        mLocale = currentConfig.locale;
866        mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
867
868        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
869        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
870        mLockPatternUtils = new LockPatternUtils(mContext);
871
872        // Connect in to the status bar manager service
873        mCommandQueue = getComponent(CommandQueue.class);
874        mCommandQueue.addCallbacks(this);
875
876        int[] switches = new int[9];
877        ArrayList<IBinder> binders = new ArrayList<>();
878        ArrayList<String> iconSlots = new ArrayList<>();
879        ArrayList<StatusBarIcon> icons = new ArrayList<>();
880        Rect fullscreenStackBounds = new Rect();
881        Rect dockedStackBounds = new Rect();
882        try {
883            mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
884                    fullscreenStackBounds, dockedStackBounds);
885        } catch (RemoteException ex) {
886            // If the system process isn't there we're doomed anyway.
887        }
888
889        createAndAddWindows();
890
891        mSettingsObserver.onChange(false); // set up
892        mCommandQueue.disable(switches[0], switches[6], false /* animate */);
893        setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
894                fullscreenStackBounds, dockedStackBounds);
895        topAppWindowChanged(switches[2] != 0);
896        // StatusBarManagerService has a back up of IME token and it's restored here.
897        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
898
899        // Set up the initial icon state
900        int N = iconSlots.size();
901        for (int i=0; i < N; i++) {
902            mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
903        }
904
905        // Set up the initial notification state.
906        try {
907            mNotificationListener.registerAsSystemService(mContext,
908                    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
909                    UserHandle.USER_ALL);
910        } catch (RemoteException e) {
911            Log.e(TAG, "Unable to register notification listener", e);
912        }
913
914
915        if (DEBUG) {
916            Log.d(TAG, String.format(
917                    "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
918                   icons.size(),
919                   switches[0],
920                   switches[1],
921                   switches[2],
922                   switches[3]
923                   ));
924        }
925
926        mCurrentUserId = ActivityManager.getCurrentUser();
927        setHeadsUpUser(mCurrentUserId);
928
929        IntentFilter filter = new IntentFilter();
930        filter.addAction(Intent.ACTION_USER_SWITCHED);
931        filter.addAction(Intent.ACTION_USER_ADDED);
932        filter.addAction(Intent.ACTION_USER_PRESENT);
933        mContext.registerReceiver(mBaseBroadcastReceiver, filter);
934
935        IntentFilter internalFilter = new IntentFilter();
936        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
937        internalFilter.addAction(BANNER_ACTION_CANCEL);
938        internalFilter.addAction(BANNER_ACTION_SETUP);
939        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
940
941        IntentFilter allUsersFilter = new IntentFilter();
942        allUsersFilter.addAction(
943                DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
944        allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
945        mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
946                null, null);
947        updateCurrentProfilesCache();
948
949        IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
950                Context.VR_SERVICE));
951        try {
952            vrManager.registerListener(mVrStateCallbacks);
953        } catch (RemoteException e) {
954            Slog.e(TAG, "Failed to register VR mode state listener: " + e);
955        }
956
957        mNonBlockablePkgs = new HashSet<>();
958        Collections.addAll(mNonBlockablePkgs, res.getStringArray(
959                com.android.internal.R.array.config_nonBlockableNotificationPackages));
960        // end old BaseStatusBar.start().
961
962        mMediaSessionManager
963                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
964        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
965        // in session state
966
967        // Lastly, call to the icon policy to install/update all the icons.
968        mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
969        mSettingsObserver.onChange(false); // set up
970
971        mHeadsUpObserver.onChange(true); // set up
972        if (ENABLE_HEADS_UP) {
973            mContext.getContentResolver().registerContentObserver(
974                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
975                    mHeadsUpObserver);
976            mContext.getContentResolver().registerContentObserver(
977                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
978                    mHeadsUpObserver);
979        }
980        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
981        mUnlockMethodCache.addListener(this);
982        startKeyguard();
983
984        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
985        putComponent(DozeHost.class, mDozeServiceHost);
986
987        notifyUserAboutHiddenNotifications();
988
989        mScreenPinningRequest = new ScreenPinningRequest(mContext);
990        mFalsingManager = FalsingManager.getInstance(mContext);
991
992        Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
993
994        Dependency.get(ConfigurationController.class).addCallback(this);
995    }
996
997    protected void createIconController() {
998    }
999
1000    // ================================================================================
1001    // Constructing the view
1002    // ================================================================================
1003    protected void makeStatusBarView() {
1004        final Context context = mContext;
1005        updateDisplaySize(); // populates mDisplayMetrics
1006        updateResources();
1007        updateTheme();
1008
1009        inflateStatusBarWindow(context);
1010        mStatusBarWindow.setService(this);
1011        mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
1012
1013        // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
1014        // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
1015        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
1016                R.id.notification_panel);
1017        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
1018                R.id.notification_stack_scroller);
1019        mNotificationPanel.setStatusBar(this);
1020        mNotificationPanel.setGroupManager(mGroupManager);
1021        mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
1022        mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
1023                R.id.notification_container_parent));
1024        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
1025
1026        mNotificationIconAreaController = SystemUIFactory.getInstance()
1027                .createNotificationIconAreaController(context, this);
1028        inflateShelf();
1029        mNotificationIconAreaController.setupShelf(mNotificationShelf);
1030        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
1031        FragmentHostManager.get(mStatusBarWindow)
1032                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
1033                    CollapsedStatusBarFragment statusBarFragment =
1034                            (CollapsedStatusBarFragment) fragment;
1035                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
1036                    mStatusBarView = (PhoneStatusBarView) fragment.getView();
1037                    mStatusBarView.setBar(this);
1038                    mStatusBarView.setPanel(mNotificationPanel);
1039                    mStatusBarView.setScrimController(mScrimController);
1040                    mStatusBarView.setBouncerShowing(mBouncerShowing);
1041                    setAreThereNotifications();
1042                    checkBarModes();
1043                }).getFragmentManager()
1044                .beginTransaction()
1045                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
1046                        CollapsedStatusBarFragment.TAG)
1047                .commit();
1048        mIconController = Dependency.get(StatusBarIconController.class);
1049
1050        mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
1051        mHeadsUpManager.setBar(this);
1052        mHeadsUpManager.addListener(this);
1053        mHeadsUpManager.addListener(mNotificationPanel);
1054        mHeadsUpManager.addListener(mGroupManager);
1055        mHeadsUpManager.addListener(mVisualStabilityManager);
1056        mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
1057        mNotificationData.setHeadsUpManager(mHeadsUpManager);
1058        mGroupManager.setHeadsUpManager(mHeadsUpManager);
1059        mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
1060
1061        if (MULTIUSER_DEBUG) {
1062            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
1063                    R.id.header_debug_info);
1064            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
1065        }
1066
1067        try {
1068            boolean showNav = mWindowManagerService.hasNavigationBar();
1069            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
1070            if (showNav) {
1071                createNavigationBar();
1072            }
1073        } catch (RemoteException ex) {
1074            // no window manager? good luck with that
1075        }
1076
1077        // figure out which pixel-format to use for the status bar.
1078        mPixelFormat = PixelFormat.OPAQUE;
1079
1080        mStackScroller.setLongPressListener(getNotificationLongClicker());
1081        mStackScroller.setStatusBar(this);
1082        mStackScroller.setGroupManager(mGroupManager);
1083        mStackScroller.setHeadsUpManager(mHeadsUpManager);
1084        mGroupManager.setOnGroupChangeListener(mStackScroller);
1085        mVisualStabilityManager.setVisibilityLocationProvider(mStackScroller);
1086
1087        inflateEmptyShadeView();
1088        inflateDismissView();
1089        mExpandedContents = mStackScroller;
1090
1091        mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
1092        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
1093        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
1094
1095        if (ENABLE_LOCKSCREEN_WALLPAPER) {
1096            mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
1097        }
1098
1099        mKeyguardIndicationController =
1100                SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
1101                (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
1102                mNotificationPanel.getLockIcon());
1103        mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
1104
1105
1106        mAmbientIndicationContainer = mStatusBarWindow.findViewById(
1107                R.id.ambient_indication_container);
1108
1109        // set the initial view visibility
1110        setAreThereNotifications();
1111
1112        // TODO: Find better place for this callback.
1113        mBatteryController.addCallback(new BatteryStateChangeCallback() {
1114            @Override
1115            public void onPowerSaveChanged(boolean isPowerSave) {
1116                mHandler.post(mCheckBarModes);
1117                if (mDozeServiceHost != null) {
1118                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
1119                }
1120            }
1121
1122            @Override
1123            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
1124                // noop
1125            }
1126        });
1127
1128        mLightBarController = Dependency.get(LightBarController.class);
1129        if (mNavigationBar != null) {
1130            mNavigationBar.setLightBarController(mLightBarController);
1131        }
1132
1133        ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
1134        ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
1135        View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
1136        mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
1137                scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
1138                scrimsVisible -> {
1139                    if (mStatusBarWindowManager != null) {
1140                        mStatusBarWindowManager.setScrimsVisible(scrimsVisible);
1141                    }
1142                });
1143        if (mScrimSrcModeEnabled) {
1144            Runnable runnable = new Runnable() {
1145                @Override
1146                public void run() {
1147                    boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
1148                    mScrimController.setDrawBehindAsSrc(asSrc);
1149                    mStackScroller.setDrawBackgroundAsSrc(asSrc);
1150                }
1151            };
1152            mBackdrop.setOnVisibilityChangedRunnable(runnable);
1153            runnable.run();
1154        }
1155        mHeadsUpManager.addListener(mScrimController);
1156        mStackScroller.setScrimController(mScrimController);
1157        mDozeScrimController = new DozeScrimController(mScrimController, context);
1158
1159        // Other icons
1160        mVolumeComponent = getComponent(VolumeComponent.class);
1161
1162        mNotificationPanel.setUserSetupComplete(mUserSetup);
1163        if (UserManager.get(mContext).isUserSwitcherEnabled()) {
1164            createUserSwitcher();
1165        }
1166
1167        // Set up the quick settings tile panel
1168        View container = mStatusBarWindow.findViewById(R.id.qs_frame);
1169        if (container != null) {
1170            FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
1171            ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
1172                    Dependency.get(ExtensionController.class).newExtension(QS.class)
1173                            .withPlugin(QS.class)
1174                            .withFeature(
1175                                    PackageManager.FEATURE_AUTOMOTIVE, () -> new CarQSFragment())
1176                            .withDefault(() -> new QSFragment())
1177                            .build());
1178            final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
1179                    mIconController);
1180            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
1181                    mScrimController);
1182            fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
1183                QS qs = (QS) f;
1184                if (qs instanceof QSFragment) {
1185                    ((QSFragment) qs).setHost(qsh);
1186                    mQSPanel = ((QSFragment) qs).getQsPanel();
1187                    mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
1188                    mKeyguardStatusBar.setQSPanel(mQSPanel);
1189                }
1190            });
1191        }
1192
1193        mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch);
1194        if (mReportRejectedTouch != null) {
1195            updateReportRejectedTouchVisibility();
1196            mReportRejectedTouch.setOnClickListener(v -> {
1197                Uri session = mFalsingManager.reportRejectedTouch();
1198                if (session == null) { return; }
1199
1200                StringWriter message = new StringWriter();
1201                message.write("Build info: ");
1202                message.write(SystemProperties.get("ro.build.description"));
1203                message.write("\nSerial number: ");
1204                message.write(SystemProperties.get("ro.serialno"));
1205                message.write("\n");
1206
1207                PrintWriter falsingPw = new PrintWriter(message);
1208                FalsingLog.dump(falsingPw);
1209                falsingPw.flush();
1210
1211                startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND)
1212                                .setType("*/*")
1213                                .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report")
1214                                .putExtra(Intent.EXTRA_STREAM, session)
1215                                .putExtra(Intent.EXTRA_TEXT, message.toString()),
1216                        "Share rejected touch report")
1217                                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
1218                        true /* onlyProvisioned */, true /* dismissShade */);
1219            });
1220        }
1221
1222        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
1223        if (!pm.isScreenOn()) {
1224            mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
1225        }
1226        mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
1227                "GestureWakeLock");
1228        mVibrator = mContext.getSystemService(Vibrator.class);
1229        int[] pattern = mContext.getResources().getIntArray(
1230                R.array.config_cameraLaunchGestureVibePattern);
1231        mCameraLaunchGestureVibePattern = new long[pattern.length];
1232        for (int i = 0; i < pattern.length; i++) {
1233            mCameraLaunchGestureVibePattern[i] = pattern[i];
1234        }
1235
1236        // receive broadcasts
1237        IntentFilter filter = new IntentFilter();
1238        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1239        filter.addAction(Intent.ACTION_SCREEN_OFF);
1240        filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
1241        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1242
1243        IntentFilter demoFilter = new IntentFilter();
1244        if (DEBUG_MEDIA_FAKE_ARTWORK) {
1245            demoFilter.addAction(ACTION_FAKE_ARTWORK);
1246        }
1247        demoFilter.addAction(ACTION_DEMO);
1248        context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
1249                android.Manifest.permission.DUMP, null);
1250
1251        // listen for USER_SETUP_COMPLETE setting (per-user)
1252        mDeviceProvisionedController.addCallback(mUserSetupObserver);
1253        mUserSetupObserver.onUserSetupChanged();
1254
1255        // disable profiling bars, since they overlap and clutter the output on app windows
1256        ThreadedRenderer.overrideProperty("disableProfileBars", "true");
1257
1258        // Private API call to make the shadows look better for Recents
1259        ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
1260    }
1261
1262    protected void createNavigationBar() {
1263        mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
1264            mNavigationBar = (NavigationBarFragment) fragment;
1265            if (mLightBarController != null) {
1266                mNavigationBar.setLightBarController(mLightBarController);
1267            }
1268            mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
1269        });
1270    }
1271
1272    /**
1273     * Returns the {@link android.view.View.OnTouchListener} that will be invoked when the
1274     * background window of the status bar is clicked.
1275     */
1276    protected View.OnTouchListener getStatusBarWindowTouchListener() {
1277        return (v, event) -> {
1278            checkUserAutohide(v, event);
1279            checkRemoteInputOutside(event);
1280            if (event.getAction() == MotionEvent.ACTION_DOWN) {
1281                if (mExpandedVisible) {
1282                    animateCollapsePanels();
1283                }
1284            }
1285            return mStatusBarWindow.onTouchEvent(event);
1286        };
1287    }
1288
1289    private void inflateShelf() {
1290        mNotificationShelf =
1291                (NotificationShelf) LayoutInflater.from(mContext).inflate(
1292                        R.layout.status_bar_notification_shelf, mStackScroller, false);
1293        mNotificationShelf.setOnActivatedListener(this);
1294        mStackScroller.setShelf(mNotificationShelf);
1295        mNotificationShelf.setOnClickListener(mGoToLockedShadeListener);
1296        mNotificationShelf.setStatusBarState(mState);
1297    }
1298
1299    public void onDensityOrFontScaleChanged() {
1300        // start old BaseStatusBar.onDensityOrFontScaleChanged().
1301        if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
1302            updateNotificationsOnDensityOrFontScaleChanged();
1303        } else {
1304            mReinflateNotificationsOnUserSwitched = true;
1305        }
1306        // end old BaseStatusBar.onDensityOrFontScaleChanged().
1307        mScrimController.onDensityOrFontScaleChanged();
1308        // TODO: Remove this.
1309        if (mStatusBarView != null) mStatusBarView.onDensityOrFontScaleChanged();
1310        if (mBrightnessMirrorController != null) {
1311            mBrightnessMirrorController.onDensityOrFontScaleChanged();
1312        }
1313        mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged();
1314        // TODO: Bring these out of StatusBar.
1315        ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
1316                .onDensityOrFontScaleChanged();
1317        Dependency.get(UserSwitcherController.class).onDensityOrFontScaleChanged();
1318        if (mKeyguardUserSwitcher != null) {
1319            mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
1320        }
1321        mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
1322
1323        reevaluateStyles();
1324    }
1325
1326    private void reinflateViews() {
1327        reevaluateStyles();
1328
1329        // Clock and bottom icons
1330        mNotificationPanel.onOverlayChanged();
1331        // The status bar on the keyguard is a special layout.
1332        if (mKeyguardStatusBar != null) mKeyguardStatusBar.onOverlayChanged();
1333        // Recreate Indication controller because internal references changed
1334        mKeyguardIndicationController =
1335                SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
1336                        mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
1337                        mNotificationPanel.getLockIcon());
1338        mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
1339        mKeyguardIndicationController
1340                .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
1341        mKeyguardIndicationController.setVisible(mState == StatusBarState.KEYGUARD);
1342        mKeyguardIndicationController.setDozing(mDozing);
1343        if (mBrightnessMirrorController != null) {
1344            mBrightnessMirrorController.onOverlayChanged();
1345        }
1346        if (mStatusBarKeyguardViewManager != null) {
1347            mStatusBarKeyguardViewManager.onOverlayChanged();
1348        }
1349        if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
1350            ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
1351        }
1352    }
1353
1354    protected void reevaluateStyles() {
1355        inflateSignalClusters();
1356        inflateDismissView();
1357        updateClearAll();
1358        inflateEmptyShadeView();
1359        updateEmptyShadeView();
1360    }
1361
1362    private void updateNotificationsOnDensityOrFontScaleChanged() {
1363        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1364        for (int i = 0; i < activeNotifications.size(); i++) {
1365            Entry entry = activeNotifications.get(i);
1366            boolean exposedGuts = mNotificationGutsExposed != null
1367                    && entry.row.getGuts() == mNotificationGutsExposed;
1368            entry.row.onDensityOrFontScaleChanged();
1369            if (exposedGuts) {
1370                mNotificationGutsExposed = entry.row.getGuts();
1371                bindGuts(entry.row, mGutsMenuItem);
1372            }
1373        }
1374    }
1375
1376    private void inflateSignalClusters() {
1377        if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar);
1378    }
1379
1380    public static SignalClusterView reinflateSignalCluster(View view) {
1381        Context context = view.getContext();
1382        SignalClusterView signalCluster =
1383                (SignalClusterView) view.findViewById(R.id.signal_cluster);
1384        if (signalCluster != null) {
1385            ViewParent parent = signalCluster.getParent();
1386            if (parent instanceof ViewGroup) {
1387                ViewGroup viewParent = (ViewGroup) parent;
1388                int index = viewParent.indexOfChild(signalCluster);
1389                viewParent.removeView(signalCluster);
1390                SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(context)
1391                        .inflate(R.layout.signal_cluster_view, viewParent, false);
1392                ViewGroup.MarginLayoutParams layoutParams =
1393                        (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams();
1394                layoutParams.setMarginsRelative(
1395                        context.getResources().getDimensionPixelSize(
1396                                R.dimen.signal_cluster_margin_start),
1397                        0, 0, 0);
1398                newCluster.setLayoutParams(layoutParams);
1399                viewParent.addView(newCluster, index);
1400                return newCluster;
1401            }
1402            return signalCluster;
1403        }
1404        return null;
1405    }
1406
1407    private void inflateEmptyShadeView() {
1408        if (mStackScroller == null) {
1409            return;
1410        }
1411        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
1412                R.layout.status_bar_no_notifications, mStackScroller, false);
1413        mStackScroller.setEmptyShadeView(mEmptyShadeView);
1414    }
1415
1416    private void inflateDismissView() {
1417        if (!mClearAllEnabled || mStackScroller == null) {
1418            return;
1419        }
1420
1421        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
1422                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
1423        mDismissView.setOnButtonClickListener(new View.OnClickListener() {
1424            @Override
1425            public void onClick(View v) {
1426                mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
1427                clearAllNotifications();
1428            }
1429        });
1430        mStackScroller.setDismissView(mDismissView);
1431    }
1432
1433    protected void createUserSwitcher() {
1434        mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
1435                (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
1436                mKeyguardStatusBar, mNotificationPanel);
1437    }
1438
1439    protected void inflateStatusBarWindow(Context context) {
1440        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
1441                R.layout.super_status_bar, null);
1442    }
1443
1444    public void clearAllNotifications() {
1445
1446        // animate-swipe all dismissable notifications, then animate the shade closed
1447        int numChildren = mStackScroller.getChildCount();
1448
1449        final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
1450        final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
1451        for (int i = 0; i < numChildren; i++) {
1452            final View child = mStackScroller.getChildAt(i);
1453            if (child instanceof ExpandableNotificationRow) {
1454                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1455                boolean parentVisible = false;
1456                boolean hasClipBounds = child.getClipBounds(mTmpRect);
1457                if (mStackScroller.canChildBeDismissed(child)) {
1458                    viewsToRemove.add(row);
1459                    if (child.getVisibility() == View.VISIBLE
1460                            && (!hasClipBounds || mTmpRect.height() > 0)) {
1461                        viewsToHide.add(child);
1462                        parentVisible = true;
1463                    }
1464                } else if (child.getVisibility() == View.VISIBLE
1465                        && (!hasClipBounds || mTmpRect.height() > 0)) {
1466                    parentVisible = true;
1467                }
1468                List<ExpandableNotificationRow> children = row.getNotificationChildren();
1469                if (children != null) {
1470                    for (ExpandableNotificationRow childRow : children) {
1471                        viewsToRemove.add(childRow);
1472                        if (parentVisible && row.areChildrenExpanded()
1473                                && mStackScroller.canChildBeDismissed(childRow)) {
1474                            hasClipBounds = childRow.getClipBounds(mTmpRect);
1475                            if (childRow.getVisibility() == View.VISIBLE
1476                                    && (!hasClipBounds || mTmpRect.height() > 0)) {
1477                                viewsToHide.add(childRow);
1478                            }
1479                        }
1480                    }
1481                }
1482            }
1483        }
1484        if (viewsToRemove.isEmpty()) {
1485            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
1486            return;
1487        }
1488
1489        addPostCollapseAction(new Runnable() {
1490            @Override
1491            public void run() {
1492                mStackScroller.setDismissAllInProgress(false);
1493                for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
1494                    if (mStackScroller.canChildBeDismissed(rowToRemove)) {
1495                        removeNotification(rowToRemove.getEntry().key, null);
1496                    } else {
1497                        rowToRemove.resetTranslation();
1498                    }
1499                }
1500                try {
1501                    mBarService.onClearAllNotifications(mCurrentUserId);
1502                } catch (Exception ex) { }
1503            }
1504        });
1505
1506        performDismissAllAnimations(viewsToHide);
1507
1508    }
1509
1510    private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
1511        Runnable animationFinishAction = new Runnable() {
1512            @Override
1513            public void run() {
1514                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
1515            }
1516        };
1517
1518        // let's disable our normal animations
1519        mStackScroller.setDismissAllInProgress(true);
1520
1521        // Decrease the delay for every row we animate to give the sense of
1522        // accelerating the swipes
1523        int rowDelayDecrement = 10;
1524        int currentDelay = 140;
1525        int totalDelay = 180;
1526        int numItems = hideAnimatedList.size();
1527        for (int i = numItems - 1; i >= 0; i--) {
1528            View view = hideAnimatedList.get(i);
1529            Runnable endRunnable = null;
1530            if (i == 0) {
1531                endRunnable = animationFinishAction;
1532            }
1533            mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260);
1534            currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
1535            totalDelay += currentDelay;
1536        }
1537    }
1538
1539    protected void setZenMode(int mode) {
1540        // start old BaseStatusBar.setZenMode().
1541        if (isDeviceProvisioned()) {
1542            mZenMode = mode;
1543            updateNotifications();
1544        }
1545        // end old BaseStatusBar.setZenMode().
1546    }
1547
1548    protected void startKeyguard() {
1549        Trace.beginSection("StatusBar#startKeyguard");
1550        KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
1551        mFingerprintUnlockController = new FingerprintUnlockController(mContext,
1552                mDozeScrimController, keyguardViewMediator,
1553                mScrimController, this, UnlockMethodCache.getInstance(mContext));
1554        mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
1555                getBouncerContainer(), mScrimController,
1556                mFingerprintUnlockController);
1557        mKeyguardIndicationController
1558                .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
1559        mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
1560        mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
1561
1562        mRemoteInputController.addCallback(new RemoteInputController.Callback() {
1563            @Override
1564            public void onRemoteInputSent(Entry entry) {
1565                if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
1566                    removeNotification(entry.key, null);
1567                } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
1568                    // We're currently holding onto this notification, but from the apps point of
1569                    // view it is already canceled, so we'll need to cancel it on the apps behalf
1570                    // after sending - unless the app posts an update in the mean time, so wait a
1571                    // bit.
1572                    mHandler.postDelayed(() -> {
1573                        if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
1574                            removeNotification(entry.key, null);
1575                        }
1576                    }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
1577                }
1578            }
1579        });
1580
1581        mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
1582        mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
1583        Trace.endSection();
1584    }
1585
1586    protected View getStatusBarView() {
1587        return mStatusBarView;
1588    }
1589
1590    public StatusBarWindowView getStatusBarWindow() {
1591        return mStatusBarWindow;
1592    }
1593
1594    protected ViewGroup getBouncerContainer() {
1595        return mStatusBarWindow;
1596    }
1597
1598    public int getStatusBarHeight() {
1599        if (mNaturalBarHeight < 0) {
1600            final Resources res = mContext.getResources();
1601            mNaturalBarHeight =
1602                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
1603        }
1604        return mNaturalBarHeight;
1605    }
1606
1607    protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
1608        if (mRecents == null) {
1609            return false;
1610        }
1611        int dockSide = WindowManagerProxy.getInstance().getDockSide();
1612        if (dockSide == WindowManager.DOCKED_INVALID) {
1613            return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
1614                    ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
1615        } else {
1616            Divider divider = getComponent(Divider.class);
1617            if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
1618                // Undocking from the minimized state is not supported
1619                return false;
1620            } else {
1621                EventBus.getDefault().send(new UndockingTaskEvent());
1622                if (metricsUndockAction != -1) {
1623                    mMetricsLogger.action(metricsUndockAction);
1624                }
1625            }
1626        }
1627        return true;
1628    }
1629
1630    void awakenDreams() {
1631        SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
1632    }
1633
1634    public UserHandle getCurrentUserHandle() {
1635        return new UserHandle(mCurrentUserId);
1636    }
1637
1638    public void addNotification(StatusBarNotification notification, RankingMap ranking)
1639            throws InflationException {
1640        String key = notification.getKey();
1641        if (DEBUG) Log.d(TAG, "addNotification key=" + key);
1642
1643        mNotificationData.updateRanking(ranking);
1644        Entry shadeEntry = createNotificationViews(notification);
1645        boolean isHeadsUped = shouldPeek(shadeEntry);
1646        if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
1647            if (shouldSuppressFullScreenIntent(key)) {
1648                if (DEBUG) {
1649                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
1650                }
1651            } else if (mNotificationData.getImportance(key)
1652                    < NotificationManager.IMPORTANCE_HIGH) {
1653                if (DEBUG) {
1654                    Log.d(TAG, "No Fullscreen intent: not important enough: "
1655                            + key);
1656                }
1657            } else {
1658                // Stop screensaver if the notification has a full-screen intent.
1659                // (like an incoming phone call)
1660                awakenDreams();
1661
1662                // not immersive & a full-screen alert should be shown
1663                if (DEBUG)
1664                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1665                try {
1666                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
1667                            key);
1668                    notification.getNotification().fullScreenIntent.send();
1669                    shadeEntry.notifyFullScreenIntentLaunched();
1670                    mMetricsLogger.count("note_fullscreen", 1);
1671                } catch (PendingIntent.CanceledException e) {
1672                }
1673            }
1674        }
1675        abortExistingInflation(key);
1676
1677        mForegroundServiceController.addNotification(notification,
1678                mNotificationData.getImportance(key));
1679
1680        mPendingNotifications.put(key, shadeEntry);
1681    }
1682
1683    private void abortExistingInflation(String key) {
1684        if (mPendingNotifications.containsKey(key)) {
1685            Entry entry = mPendingNotifications.get(key);
1686            entry.abortTask();
1687            mPendingNotifications.remove(key);
1688        }
1689        Entry addedEntry = mNotificationData.get(key);
1690        if (addedEntry != null) {
1691            addedEntry.abortTask();
1692        }
1693    }
1694
1695    private void addEntry(Entry shadeEntry) {
1696        boolean isHeadsUped = shouldPeek(shadeEntry);
1697        if (isHeadsUped) {
1698            mHeadsUpManager.showNotification(shadeEntry);
1699            // Mark as seen immediately
1700            setNotificationShown(shadeEntry.notification);
1701        }
1702        addNotificationViews(shadeEntry);
1703        // Recalculate the position of the sliding windows and the titles.
1704        setAreThereNotifications();
1705    }
1706
1707    @Override
1708    public void handleInflationException(StatusBarNotification notification, Exception e) {
1709        handleNotificationError(notification, e.getMessage());
1710    }
1711
1712    @Override
1713    public void onAsyncInflationFinished(Entry entry) {
1714        mPendingNotifications.remove(entry.key);
1715        // If there was an async task started after the removal, we don't want to add it back to
1716        // the list, otherwise we might get leaks.
1717        boolean isNew = mNotificationData.get(entry.key) == null;
1718        if (isNew && !entry.row.isRemoved()) {
1719            addEntry(entry);
1720        } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
1721            mVisualStabilityManager.onLowPriorityUpdated(entry);
1722            updateNotificationShade();
1723        }
1724        entry.row.setLowPriorityStateUpdated(false);
1725    }
1726
1727    private boolean shouldSuppressFullScreenIntent(String key) {
1728        if (isDeviceInVrMode()) {
1729            return true;
1730        }
1731
1732        if (mPowerManager.isInteractive()) {
1733            return mNotificationData.shouldSuppressScreenOn(key);
1734        } else {
1735            return mNotificationData.shouldSuppressScreenOff(key);
1736        }
1737    }
1738
1739    protected void updateNotificationRanking(RankingMap ranking) {
1740        mNotificationData.updateRanking(ranking);
1741        updateNotifications();
1742    }
1743
1744    public void removeNotification(String key, RankingMap ranking) {
1745        boolean deferRemoval = false;
1746        abortExistingInflation(key);
1747        if (mHeadsUpManager.isHeadsUp(key)) {
1748            // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
1749            // sending look longer than it takes.
1750            // Also we should not defer the removal if reordering isn't allowed since otherwise
1751            // some notifications can't disappear before the panel is closed.
1752            boolean ignoreEarliestRemovalTime = mRemoteInputController.isSpinning(key)
1753                    && !FORCE_REMOTE_INPUT_HISTORY
1754                    || !mVisualStabilityManager.isReorderingAllowed();
1755            deferRemoval = !mHeadsUpManager.removeNotification(key,  ignoreEarliestRemovalTime);
1756        }
1757        if (key.equals(mMediaNotificationKey)) {
1758            clearCurrentMediaNotification();
1759            updateMediaMetaData(true, true);
1760        }
1761        if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) {
1762            Entry entry = mNotificationData.get(key);
1763            StatusBarNotification sbn = entry.notification;
1764
1765            Notification.Builder b = Notification.Builder
1766                    .recoverBuilder(mContext, sbn.getNotification().clone());
1767            CharSequence[] oldHistory = sbn.getNotification().extras
1768                    .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
1769            CharSequence[] newHistory;
1770            if (oldHistory == null) {
1771                newHistory = new CharSequence[1];
1772            } else {
1773                newHistory = new CharSequence[oldHistory.length + 1];
1774                for (int i = 0; i < oldHistory.length; i++) {
1775                    newHistory[i + 1] = oldHistory[i];
1776                }
1777            }
1778            newHistory[0] = String.valueOf(entry.remoteInputText);
1779            b.setRemoteInputHistory(newHistory);
1780
1781            Notification newNotification = b.build();
1782
1783            // Undo any compatibility view inflation
1784            newNotification.contentView = sbn.getNotification().contentView;
1785            newNotification.bigContentView = sbn.getNotification().bigContentView;
1786            newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
1787
1788            StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
1789                    sbn.getOpPkg(),
1790                    sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1791                    newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
1792            boolean updated = false;
1793            try {
1794                updateNotification(newSbn, null);
1795                updated = true;
1796            } catch (InflationException e) {
1797                deferRemoval = false;
1798            }
1799            if (updated) {
1800                mKeysKeptForRemoteInput.add(entry.key);
1801                return;
1802            }
1803        }
1804        if (deferRemoval) {
1805            mLatestRankingMap = ranking;
1806            mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
1807            return;
1808        }
1809        Entry entry = mNotificationData.get(key);
1810
1811        if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
1812                && (entry.row != null && !entry.row.isDismissed())) {
1813            mLatestRankingMap = ranking;
1814            mRemoteInputEntriesToRemoveOnCollapse.add(entry);
1815            return;
1816        }
1817        if (entry != null && mNotificationGutsExposed != null
1818                && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
1819                && !entry.row.getGuts().isLeavebehind()) {
1820            Log.w(TAG, "Keeping notification because it's showing guts. " + key);
1821            mLatestRankingMap = ranking;
1822            mKeyToRemoveOnGutsClosed = key;
1823            return;
1824        }
1825
1826        if (entry != null) {
1827            mForegroundServiceController.removeNotification(entry.notification);
1828        }
1829
1830        if (entry != null && entry.row != null) {
1831            entry.row.setRemoved();
1832            mStackScroller.cleanUpViewState(entry.row);
1833        }
1834        // Let's remove the children if this was a summary
1835        handleGroupSummaryRemoved(key, ranking);
1836        StatusBarNotification old = removeNotificationViews(key, ranking);
1837        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1838
1839        if (old != null) {
1840            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
1841                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
1842                if (mState == StatusBarState.SHADE) {
1843                    animateCollapsePanels();
1844                } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
1845                    goToKeyguard();
1846                }
1847            }
1848        }
1849        setAreThereNotifications();
1850    }
1851
1852    /**
1853     * Ensures that the group children are cancelled immediately when the group summary is cancelled
1854     * instead of waiting for the notification manager to send all cancels. Otherwise this could
1855     * lead to flickers.
1856     *
1857     * This also ensures that the animation looks nice and only consists of a single disappear
1858     * animation instead of multiple.
1859     *
1860     * @param key the key of the notification was removed
1861     * @param ranking the current ranking
1862     */
1863    private void handleGroupSummaryRemoved(String key,
1864            RankingMap ranking) {
1865        Entry entry = mNotificationData.get(key);
1866        if (entry != null && entry.row != null
1867                && entry.row.isSummaryWithChildren()) {
1868            if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
1869                // We don't want to remove children for autobundled notifications as they are not
1870                // always cancelled. We only remove them if they were dismissed by the user.
1871                return;
1872            }
1873            List<ExpandableNotificationRow> notificationChildren =
1874                    entry.row.getNotificationChildren();
1875            ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
1876            for (int i = 0; i < notificationChildren.size(); i++) {
1877                ExpandableNotificationRow row = notificationChildren.get(i);
1878                if ((row.getStatusBarNotification().getNotification().flags
1879                        & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1880                    // the child is a forground service notification which we can't remove!
1881                    continue;
1882                }
1883                toRemove.add(row);
1884                row.setKeepInParent(true);
1885                // we need to set this state earlier as otherwise we might generate some weird
1886                // animations
1887                row.setRemoved();
1888            }
1889        }
1890    }
1891
1892    protected void performRemoveNotification(StatusBarNotification n) {
1893        Entry entry = mNotificationData.get(n.getKey());
1894        if (mRemoteInputController.isRemoteInputActive(entry)) {
1895            mRemoteInputController.removeRemoteInput(entry, null);
1896        }
1897        // start old BaseStatusBar.performRemoveNotification.
1898        final String pkg = n.getPackageName();
1899        final String tag = n.getTag();
1900        final int id = n.getId();
1901        final int userId = n.getUserId();
1902        try {
1903            mBarService.onNotificationClear(pkg, tag, id, userId);
1904            if (FORCE_REMOTE_INPUT_HISTORY
1905                    && mKeysKeptForRemoteInput.contains(n.getKey())) {
1906                mKeysKeptForRemoteInput.remove(n.getKey());
1907            }
1908            removeNotification(n.getKey(), null);
1909
1910        } catch (RemoteException ex) {
1911            // system process is dead if we're here.
1912        }
1913        if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
1914            // We were showing a pulse for a notification, but no notifications are pulsing anymore.
1915            // Finish the pulse.
1916            mDozeScrimController.pulseOutNow();
1917        }
1918        // end old BaseStatusBar.performRemoveNotification.
1919    }
1920
1921    private void updateNotificationShade() {
1922        if (mStackScroller == null) return;
1923
1924        // Do not modify the notifications during collapse.
1925        if (isCollapsing()) {
1926            addPostCollapseAction(new Runnable() {
1927                @Override
1928                public void run() {
1929                    updateNotificationShade();
1930                }
1931            });
1932            return;
1933        }
1934
1935        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1936        ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
1937        final int N = activeNotifications.size();
1938        for (int i=0; i<N; i++) {
1939            Entry ent = activeNotifications.get(i);
1940            if (ent.row.isDismissed() || ent.row.isRemoved()) {
1941                // we don't want to update removed notifications because they could
1942                // temporarily become children if they were isolated before.
1943                continue;
1944            }
1945            int userId = ent.notification.getUserId();
1946
1947            // Display public version of the notification if we need to redact.
1948            boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);
1949            boolean userPublic = devicePublic || isLockscreenPublicMode(userId);
1950            boolean needsRedaction = needsRedaction(ent);
1951            boolean sensitive = userPublic && needsRedaction;
1952            boolean deviceSensitive = devicePublic
1953                    && !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
1954            ent.row.setSensitive(sensitive, deviceSensitive);
1955            ent.row.setNeedsRedaction(needsRedaction);
1956            if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
1957                ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
1958                        ent.row.getStatusBarNotification());
1959                List<ExpandableNotificationRow> orderedChildren =
1960                        mTmpChildOrderMap.get(summary);
1961                if (orderedChildren == null) {
1962                    orderedChildren = new ArrayList<>();
1963                    mTmpChildOrderMap.put(summary, orderedChildren);
1964                }
1965                orderedChildren.add(ent.row);
1966            } else {
1967                toShow.add(ent.row);
1968            }
1969
1970        }
1971
1972        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
1973        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1974            View child = mStackScroller.getChildAt(i);
1975            if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1976                toRemove.add((ExpandableNotificationRow) child);
1977            }
1978        }
1979
1980        for (ExpandableNotificationRow remove : toRemove) {
1981            if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
1982                // we are only transfering this notification to its parent, don't generate an animation
1983                mStackScroller.setChildTransferInProgress(true);
1984            }
1985            if (remove.isSummaryWithChildren()) {
1986                remove.removeAllChildren();
1987            }
1988            mStackScroller.removeView(remove);
1989            mStackScroller.setChildTransferInProgress(false);
1990        }
1991
1992        removeNotificationChildren();
1993
1994        for (int i=0; i<toShow.size(); i++) {
1995            View v = toShow.get(i);
1996            if (v.getParent() == null) {
1997                mVisualStabilityManager.notifyViewAddition(v);
1998                mStackScroller.addView(v);
1999            }
2000        }
2001
2002        addNotificationChildrenAndSort();
2003
2004        // So after all this work notifications still aren't sorted correctly.
2005        // Let's do that now by advancing through toShow and mStackScroller in
2006        // lock-step, making sure mStackScroller matches what we see in toShow.
2007        int j = 0;
2008        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
2009            View child = mStackScroller.getChildAt(i);
2010            if (!(child instanceof ExpandableNotificationRow)) {
2011                // We don't care about non-notification views.
2012                continue;
2013            }
2014
2015            ExpandableNotificationRow targetChild = toShow.get(j);
2016            if (child != targetChild) {
2017                // Oops, wrong notification at this position. Put the right one
2018                // here and advance both lists.
2019                if (mVisualStabilityManager.canReorderNotification(targetChild)) {
2020                    mStackScroller.changeViewPosition(targetChild, i);
2021                } else {
2022                    mVisualStabilityManager.addReorderingAllowedCallback(this);
2023                }
2024            }
2025            j++;
2026
2027        }
2028
2029        mVisualStabilityManager.onReorderingFinished();
2030        // clear the map again for the next usage
2031        mTmpChildOrderMap.clear();
2032
2033        updateRowStates();
2034        updateSpeedBumpIndex();
2035        updateClearAll();
2036        updateEmptyShadeView();
2037
2038        updateQsExpansionEnabled();
2039
2040        // Let's also update the icons
2041        mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
2042    }
2043
2044    /** @return true if the entry needs redaction when on the lockscreen. */
2045    private boolean needsRedaction(Entry ent) {
2046        int userId = ent.notification.getUserId();
2047
2048        boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
2049        boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
2050        boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
2051
2052        boolean notificationRequestsRedaction =
2053                ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
2054        boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
2055
2056        return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
2057    }
2058
2059    /**
2060     * Disable QS if device not provisioned.
2061     * If the user switcher is simple then disable QS during setup because
2062     * the user intends to use the lock screen user switcher, QS in not needed.
2063     */
2064    private void updateQsExpansionEnabled() {
2065        mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
2066                && (mUserSetup || mUserSwitcherController == null
2067                        || !mUserSwitcherController.isSimpleUserSwitcher())
2068                && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
2069                && !mDozing
2070                && !ONLY_CORE_APPS);
2071    }
2072
2073    private void addNotificationChildrenAndSort() {
2074        // Let's now add all notification children which are missing
2075        boolean orderChanged = false;
2076        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
2077            View view = mStackScroller.getChildAt(i);
2078            if (!(view instanceof ExpandableNotificationRow)) {
2079                // We don't care about non-notification views.
2080                continue;
2081            }
2082
2083            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
2084            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
2085            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
2086
2087            for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
2088                    childIndex++) {
2089                ExpandableNotificationRow childView = orderedChildren.get(childIndex);
2090                if (children == null || !children.contains(childView)) {
2091                    if (childView.getParent() != null) {
2092                        Log.wtf(TAG, "trying to add a notification child that already has " +
2093                                "a parent. class:" + childView.getParent().getClass() +
2094                                "\n child: " + childView);
2095                        // This shouldn't happen. We can recover by removing it though.
2096                        ((ViewGroup) childView.getParent()).removeView(childView);
2097                    }
2098                    mVisualStabilityManager.notifyViewAddition(childView);
2099                    parent.addChildNotification(childView, childIndex);
2100                    mStackScroller.notifyGroupChildAdded(childView);
2101                }
2102            }
2103
2104            // Finally after removing and adding has been beformed we can apply the order.
2105            orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager, this);
2106        }
2107        if (orderChanged) {
2108            mStackScroller.generateChildOrderChangedEvent();
2109        }
2110    }
2111
2112    private void removeNotificationChildren() {
2113        // First let's remove all children which don't belong in the parents
2114        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
2115        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
2116            View view = mStackScroller.getChildAt(i);
2117            if (!(view instanceof ExpandableNotificationRow)) {
2118                // We don't care about non-notification views.
2119                continue;
2120            }
2121
2122            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
2123            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
2124            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
2125
2126            if (children != null) {
2127                toRemove.clear();
2128                for (ExpandableNotificationRow childRow : children) {
2129                    if ((orderedChildren == null
2130                            || !orderedChildren.contains(childRow))
2131                            && !childRow.keepInParent()) {
2132                        toRemove.add(childRow);
2133                    }
2134                }
2135                for (ExpandableNotificationRow remove : toRemove) {
2136                    parent.removeChildNotification(remove);
2137                    if (mNotificationData.get(remove.getStatusBarNotification().getKey()) == null) {
2138                        // We only want to add an animation if the view is completely removed
2139                        // otherwise it's just a transfer
2140                        mStackScroller.notifyGroupChildRemoved(remove,
2141                                parent.getChildrenContainer());
2142                    }
2143                }
2144            }
2145        }
2146    }
2147
2148    public void addQsTile(ComponentName tile) {
2149        mQSPanel.getHost().addTile(tile);
2150    }
2151
2152    public void remQsTile(ComponentName tile) {
2153        mQSPanel.getHost().removeTile(tile);
2154    }
2155
2156    public void clickTile(ComponentName tile) {
2157        mQSPanel.clickTile(tile);
2158    }
2159
2160    private boolean packageHasVisibilityOverride(String key) {
2161        return mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_PRIVATE;
2162    }
2163
2164    private void updateClearAll() {
2165        if (!mClearAllEnabled) {
2166            return;
2167        }
2168        boolean showDismissView = mState != StatusBarState.KEYGUARD
2169                && hasActiveClearableNotifications();
2170        mStackScroller.updateDismissView(showDismissView);
2171    }
2172
2173    /**
2174     * Return whether there are any clearable notifications
2175     */
2176    private boolean hasActiveClearableNotifications() {
2177        int childCount = mStackScroller.getChildCount();
2178        for (int i = 0; i < childCount; i++) {
2179            View child = mStackScroller.getChildAt(i);
2180            if (!(child instanceof ExpandableNotificationRow)) {
2181                continue;
2182            }
2183            if (((ExpandableNotificationRow) child).canViewBeDismissed()) {
2184                    return true;
2185            }
2186        }
2187        return false;
2188    }
2189
2190    private void updateEmptyShadeView() {
2191        boolean showEmptyShadeView =
2192                mState != StatusBarState.KEYGUARD &&
2193                        mNotificationData.getActiveNotifications().size() == 0;
2194        mNotificationPanel.showEmptyShadeView(showEmptyShadeView);
2195    }
2196
2197    private void updateSpeedBumpIndex() {
2198        int speedBumpIndex = 0;
2199        int currentIndex = 0;
2200        final int N = mStackScroller.getChildCount();
2201        for (int i = 0; i < N; i++) {
2202            View view = mStackScroller.getChildAt(i);
2203            if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
2204                continue;
2205            }
2206            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
2207            currentIndex++;
2208            if (!mNotificationData.isAmbient(row.getStatusBarNotification().getKey())) {
2209                speedBumpIndex = currentIndex;
2210            }
2211        }
2212        boolean noAmbient = speedBumpIndex == N;
2213        mStackScroller.updateSpeedBumpIndex(speedBumpIndex, noAmbient);
2214    }
2215
2216    public static boolean isTopLevelChild(Entry entry) {
2217        return entry.row.getParent() instanceof NotificationStackScrollLayout;
2218    }
2219
2220    protected void updateNotifications() {
2221        mNotificationData.filterAndSort();
2222
2223        updateNotificationShade();
2224    }
2225
2226    public void requestNotificationUpdate() {
2227        updateNotifications();
2228    }
2229
2230    protected void setAreThereNotifications() {
2231
2232        if (SPEW) {
2233            final boolean clearable = hasActiveNotifications() &&
2234                    hasActiveClearableNotifications();
2235            Log.d(TAG, "setAreThereNotifications: N=" +
2236                    mNotificationData.getActiveNotifications().size() + " any=" +
2237                    hasActiveNotifications() + " clearable=" + clearable);
2238        }
2239
2240        if (mStatusBarView != null) {
2241            final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
2242            final boolean showDot = hasActiveNotifications() && !areLightsOn();
2243            if (showDot != (nlo.getAlpha() == 1.0f)) {
2244                if (showDot) {
2245                    nlo.setAlpha(0f);
2246                    nlo.setVisibility(View.VISIBLE);
2247                }
2248                nlo.animate()
2249                        .alpha(showDot ? 1 : 0)
2250                        .setDuration(showDot ? 750 : 250)
2251                        .setInterpolator(new AccelerateInterpolator(2.0f))
2252                        .setListener(showDot ? null : new AnimatorListenerAdapter() {
2253                            @Override
2254                            public void onAnimationEnd(Animator _a) {
2255                                nlo.setVisibility(View.GONE);
2256                            }
2257                        })
2258                        .start();
2259            }
2260        }
2261
2262        findAndUpdateMediaNotifications();
2263    }
2264
2265    public void findAndUpdateMediaNotifications() {
2266        boolean metaDataChanged = false;
2267
2268        synchronized (mNotificationData) {
2269            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
2270            final int N = activeNotifications.size();
2271
2272            // Promote the media notification with a controller in 'playing' state, if any.
2273            Entry mediaNotification = null;
2274            MediaController controller = null;
2275            for (int i = 0; i < N; i++) {
2276                final Entry entry = activeNotifications.get(i);
2277                if (isMediaNotification(entry)) {
2278                    final MediaSession.Token token =
2279                            entry.notification.getNotification().extras
2280                            .getParcelable(Notification.EXTRA_MEDIA_SESSION);
2281                    if (token != null) {
2282                        MediaController aController = new MediaController(mContext, token);
2283                        if (PlaybackState.STATE_PLAYING ==
2284                                getMediaControllerPlaybackState(aController)) {
2285                            if (DEBUG_MEDIA) {
2286                                Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
2287                                        + entry.notification.getKey());
2288                            }
2289                            mediaNotification = entry;
2290                            controller = aController;
2291                            break;
2292                        }
2293                    }
2294                }
2295            }
2296            if (mediaNotification == null) {
2297                // Still nothing? OK, let's just look for live media sessions and see if they match
2298                // one of our notifications. This will catch apps that aren't (yet!) using media
2299                // notifications.
2300
2301                if (mMediaSessionManager != null) {
2302                    final List<MediaController> sessions
2303                            = mMediaSessionManager.getActiveSessionsForUser(
2304                                    null,
2305                                    UserHandle.USER_ALL);
2306
2307                    for (MediaController aController : sessions) {
2308                        if (PlaybackState.STATE_PLAYING ==
2309                                getMediaControllerPlaybackState(aController)) {
2310                            // now to see if we have one like this
2311                            final String pkg = aController.getPackageName();
2312
2313                            for (int i = 0; i < N; i++) {
2314                                final Entry entry = activeNotifications.get(i);
2315                                if (entry.notification.getPackageName().equals(pkg)) {
2316                                    if (DEBUG_MEDIA) {
2317                                        Log.v(TAG, "DEBUG_MEDIA: found controller matching "
2318                                            + entry.notification.getKey());
2319                                    }
2320                                    controller = aController;
2321                                    mediaNotification = entry;
2322                                    break;
2323                                }
2324                            }
2325                        }
2326                    }
2327                }
2328            }
2329
2330            if (controller != null && !sameSessions(mMediaController, controller)) {
2331                // We have a new media session
2332                clearCurrentMediaNotification();
2333                mMediaController = controller;
2334                mMediaController.registerCallback(mMediaListener);
2335                mMediaMetadata = mMediaController.getMetadata();
2336                if (DEBUG_MEDIA) {
2337                    Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
2338                            + mMediaMetadata);
2339                }
2340
2341                if (mediaNotification != null) {
2342                    mMediaNotificationKey = mediaNotification.notification.getKey();
2343                    if (DEBUG_MEDIA) {
2344                        Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
2345                                + mMediaNotificationKey + " controller=" + mMediaController);
2346                    }
2347                }
2348                metaDataChanged = true;
2349            }
2350        }
2351
2352        if (metaDataChanged) {
2353            updateNotifications();
2354        }
2355        updateMediaMetaData(metaDataChanged, true);
2356    }
2357
2358    private int getMediaControllerPlaybackState(MediaController controller) {
2359        if (controller != null) {
2360            final PlaybackState playbackState = controller.getPlaybackState();
2361            if (playbackState != null) {
2362                return playbackState.getState();
2363            }
2364        }
2365        return PlaybackState.STATE_NONE;
2366    }
2367
2368    private boolean isPlaybackActive(int state) {
2369        if (state != PlaybackState.STATE_STOPPED
2370                && state != PlaybackState.STATE_ERROR
2371                && state != PlaybackState.STATE_NONE) {
2372            return true;
2373        }
2374        return false;
2375    }
2376
2377    private void clearCurrentMediaNotification() {
2378        mMediaNotificationKey = null;
2379        mMediaMetadata = null;
2380        if (mMediaController != null) {
2381            if (DEBUG_MEDIA) {
2382                Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
2383                        + mMediaController.getPackageName());
2384            }
2385            mMediaController.unregisterCallback(mMediaListener);
2386        }
2387        mMediaController = null;
2388    }
2389
2390    private boolean sameSessions(MediaController a, MediaController b) {
2391        if (a == b) return true;
2392        if (a == null) return false;
2393        return a.controlsSameSession(b);
2394    }
2395
2396    /**
2397     * Hide the album artwork that is fading out and release its bitmap.
2398     */
2399    protected Runnable mHideBackdropFront = new Runnable() {
2400        @Override
2401        public void run() {
2402            if (DEBUG_MEDIA) {
2403                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
2404            }
2405            mBackdropFront.setVisibility(View.INVISIBLE);
2406            mBackdropFront.animate().cancel();
2407            mBackdropFront.setImageDrawable(null);
2408        }
2409    };
2410
2411    /**
2412     * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
2413     */
2414    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
2415        Trace.beginSection("StatusBar#updateMediaMetaData");
2416        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
2417            Trace.endSection();
2418            return;
2419        }
2420
2421        if (mBackdrop == null) {
2422            Trace.endSection();
2423            return; // called too early
2424        }
2425
2426        if (mLaunchTransitionFadingAway) {
2427            mBackdrop.setVisibility(View.INVISIBLE);
2428            Trace.endSection();
2429            return;
2430        }
2431
2432        if (DEBUG_MEDIA) {
2433            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
2434                    + " metadata=" + mMediaMetadata
2435                    + " metaDataChanged=" + metaDataChanged
2436                    + " state=" + mState);
2437        }
2438
2439        Drawable artworkDrawable = null;
2440        if (mMediaMetadata != null) {
2441            Bitmap artworkBitmap = null;
2442            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
2443            if (artworkBitmap == null) {
2444                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
2445                // might still be null
2446            }
2447            if (artworkBitmap != null) {
2448                artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
2449            }
2450        }
2451        boolean allowWhenShade = false;
2452        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
2453            Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap();
2454            if (lockWallpaper != null) {
2455                artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
2456                        mBackdropBack.getResources(), lockWallpaper);
2457                // We're in the SHADE mode on the SIM screen - yet we still need to show
2458                // the lockscreen wallpaper in that mode.
2459                allowWhenShade = mStatusBarKeyguardViewManager != null
2460                        && mStatusBarKeyguardViewManager.isShowing();
2461            }
2462        }
2463
2464        boolean hideBecauseOccluded = mStatusBarKeyguardViewManager != null
2465                && mStatusBarKeyguardViewManager.isOccluded();
2466
2467        final boolean hasArtwork = artworkDrawable != null;
2468
2469        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
2470                && (mState != StatusBarState.SHADE || allowWhenShade)
2471                && mFingerprintUnlockController.getMode()
2472                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
2473                && !hideBecauseOccluded) {
2474            // time to show some art!
2475            if (mBackdrop.getVisibility() != View.VISIBLE) {
2476                mBackdrop.setVisibility(View.VISIBLE);
2477                if (allowEnterAnimation) {
2478                    mBackdrop.setAlpha(SRC_MIN_ALPHA);
2479                    mBackdrop.animate().alpha(1f);
2480                } else {
2481                    mBackdrop.animate().cancel();
2482                    mBackdrop.setAlpha(1f);
2483                }
2484                mStatusBarWindowManager.setBackdropShowing(true);
2485                metaDataChanged = true;
2486                if (DEBUG_MEDIA) {
2487                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
2488                }
2489            }
2490            if (metaDataChanged) {
2491                if (mBackdropBack.getDrawable() != null) {
2492                    Drawable drawable =
2493                            mBackdropBack.getDrawable().getConstantState()
2494                                    .newDrawable(mBackdropFront.getResources()).mutate();
2495                    mBackdropFront.setImageDrawable(drawable);
2496                    if (mScrimSrcModeEnabled) {
2497                        mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
2498                    }
2499                    mBackdropFront.setAlpha(1f);
2500                    mBackdropFront.setVisibility(View.VISIBLE);
2501                } else {
2502                    mBackdropFront.setVisibility(View.INVISIBLE);
2503                }
2504
2505                if (DEBUG_MEDIA_FAKE_ARTWORK) {
2506                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
2507                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
2508                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
2509                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
2510                } else {
2511                    mBackdropBack.setImageDrawable(artworkDrawable);
2512                }
2513                if (mScrimSrcModeEnabled) {
2514                    mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
2515                }
2516
2517                if (mBackdropFront.getVisibility() == View.VISIBLE) {
2518                    if (DEBUG_MEDIA) {
2519                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
2520                                + mBackdropFront.getDrawable()
2521                                + " to "
2522                                + mBackdropBack.getDrawable());
2523                    }
2524                    mBackdropFront.animate()
2525                            .setDuration(250)
2526                            .alpha(0f).withEndAction(mHideBackdropFront);
2527                }
2528            }
2529        } else {
2530            // need to hide the album art, either because we are unlocked or because
2531            // the metadata isn't there to support it
2532            if (mBackdrop.getVisibility() != View.GONE) {
2533                if (DEBUG_MEDIA) {
2534                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
2535                }
2536                if (mFingerprintUnlockController.getMode()
2537                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
2538                        || hideBecauseOccluded) {
2539
2540                    // We are unlocking directly - no animation!
2541                    mBackdrop.setVisibility(View.GONE);
2542                    mBackdropBack.setImageDrawable(null);
2543                    mStatusBarWindowManager.setBackdropShowing(false);
2544                } else {
2545                    mStatusBarWindowManager.setBackdropShowing(false);
2546                    mBackdrop.animate()
2547                            .alpha(SRC_MIN_ALPHA)
2548                            .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
2549                            .setDuration(300)
2550                            .setStartDelay(0)
2551                            .withEndAction(new Runnable() {
2552                                @Override
2553                                public void run() {
2554                                    mBackdrop.setVisibility(View.GONE);
2555                                    mBackdropFront.animate().cancel();
2556                                    mBackdropBack.setImageDrawable(null);
2557                                    mHandler.post(mHideBackdropFront);
2558                                }
2559                            });
2560                    if (mKeyguardFadingAway) {
2561                        mBackdrop.animate()
2562                                // Make it disappear faster, as the focus should be on the activity
2563                                // behind.
2564                                .setDuration(mKeyguardFadingAwayDuration / 2)
2565                                .setStartDelay(mKeyguardFadingAwayDelay)
2566                                .setInterpolator(Interpolators.LINEAR)
2567                                .start();
2568                    }
2569                }
2570            }
2571        }
2572        Trace.endSection();
2573    }
2574
2575    private void updateReportRejectedTouchVisibility() {
2576        if (mReportRejectedTouch == null) {
2577            return;
2578        }
2579        mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD
2580                && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
2581    }
2582
2583    /**
2584     * State is one or more of the DISABLE constants from StatusBarManager.
2585     */
2586    @Override
2587    public void disable(int state1, int state2, boolean animate) {
2588        animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
2589        mDisabledUnmodified1 = state1;
2590        mDisabledUnmodified2 = state2;
2591        final int old1 = mDisabled1;
2592        final int diff1 = state1 ^ old1;
2593        mDisabled1 = state1;
2594
2595        final int old2 = mDisabled2;
2596        final int diff2 = state2 ^ old2;
2597        mDisabled2 = state2;
2598
2599        if (DEBUG) {
2600            Log.d(TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
2601                old1, state1, diff1));
2602            Log.d(TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
2603                old2, state2, diff2));
2604        }
2605
2606        StringBuilder flagdbg = new StringBuilder();
2607        flagdbg.append("disable<");
2608        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND))                ? 'E' : 'e');
2609        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_EXPAND))                ? '!' : ' ');
2610        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS))    ? 'I' : 'i');
2611        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ICONS))    ? '!' : ' ');
2612        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS))   ? 'A' : 'a');
2613        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS))   ? '!' : ' ');
2614        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO))           ? 'S' : 's');
2615        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_SYSTEM_INFO))           ? '!' : ' ');
2616        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK))                  ? 'B' : 'b');
2617        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_BACK))                  ? '!' : ' ');
2618        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME))                  ? 'H' : 'h');
2619        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_HOME))                  ? '!' : ' ');
2620        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT))                ? 'R' : 'r');
2621        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_RECENT))                ? '!' : ' ');
2622        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK))                 ? 'C' : 'c');
2623        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_CLOCK))                 ? '!' : ' ');
2624        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH))                ? 'S' : 's');
2625        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_SEARCH))                ? '!' : ' ');
2626        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS))       ? 'Q' : 'q');
2627        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_QUICK_SETTINGS))       ? '!' : ' ');
2628        flagdbg.append('>');
2629        Log.d(TAG, flagdbg.toString());
2630
2631        if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
2632            if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
2633                animateCollapsePanels();
2634            }
2635        }
2636
2637        if ((diff1 & StatusBarManager.DISABLE_RECENT) != 0) {
2638            if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) {
2639                // close recents if it's visible
2640                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2641                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2642            }
2643        }
2644
2645        if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
2646            mDisableNotificationAlerts =
2647                    (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
2648            mHeadsUpObserver.onChange(true);
2649        }
2650
2651        if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
2652            updateQsExpansionEnabled();
2653        }
2654    }
2655
2656    /**
2657     * Reapplies the disable flags as last requested by StatusBarManager.
2658     *
2659     * This needs to be called if state used by {@link #adjustDisableFlags} changes.
2660     */
2661    public void recomputeDisableFlags(boolean animate) {
2662        mCommandQueue.recomputeDisableFlags(animate);
2663    }
2664
2665    protected H createHandler() {
2666        return new StatusBar.H();
2667    }
2668
2669    @Override
2670    public void startActivity(Intent intent, boolean dismissShade) {
2671        startActivityDismissingKeyguard(intent, false, dismissShade);
2672    }
2673
2674    @Override
2675    public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
2676        startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade);
2677    }
2678
2679    @Override
2680    public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
2681        startActivityDismissingKeyguard(intent, false, dismissShade,
2682                false /* disallowEnterPictureInPictureWhileLaunching */, callback);
2683    }
2684
2685    public void setQsExpanded(boolean expanded) {
2686        mStatusBarWindowManager.setQsExpanded(expanded);
2687        mNotificationPanel.setStatusAccessibilityImportance(expanded
2688                ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
2689                : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
2690    }
2691
2692    public boolean isGoingToNotificationShade() {
2693        return mLeaveOpenOnKeyguardHide;
2694    }
2695
2696    public boolean isWakeUpComingFromTouch() {
2697        return mWakeUpComingFromTouch;
2698    }
2699
2700    public boolean isFalsingThresholdNeeded() {
2701        return getBarState() == StatusBarState.KEYGUARD;
2702    }
2703
2704    public boolean isDozing() {
2705        return mDozing;
2706    }
2707
2708    @Override  // NotificationData.Environment
2709    public String getCurrentMediaNotificationKey() {
2710        return mMediaNotificationKey;
2711    }
2712
2713    public boolean isScrimSrcModeEnabled() {
2714        return mScrimSrcModeEnabled;
2715    }
2716
2717    /**
2718     * To be called when there's a state change in StatusBarKeyguardViewManager.
2719     */
2720    public void onKeyguardViewManagerStatesUpdated() {
2721        logStateToEventlog();
2722    }
2723
2724    @Override  // UnlockMethodCache.OnUnlockMethodChangedListener
2725    public void onUnlockMethodStateChanged() {
2726        logStateToEventlog();
2727    }
2728
2729    @Override
2730    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
2731        if (inPinnedMode) {
2732            mStatusBarWindowManager.setHeadsUpShowing(true);
2733            mStatusBarWindowManager.setForceStatusBarVisible(true);
2734            if (mNotificationPanel.isFullyCollapsed()) {
2735                // We need to ensure that the touchable region is updated before the window will be
2736                // resized, in order to not catch any touches. A layout will ensure that
2737                // onComputeInternalInsets will be called and after that we can resize the layout. Let's
2738                // make sure that the window stays small for one frame until the touchableRegion is set.
2739                mNotificationPanel.requestLayout();
2740                mStatusBarWindowManager.setForceWindowCollapsed(true);
2741                mNotificationPanel.post(new Runnable() {
2742                    @Override
2743                    public void run() {
2744                        mStatusBarWindowManager.setForceWindowCollapsed(false);
2745                    }
2746                });
2747            }
2748        } else {
2749            if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) {
2750                // We are currently tracking or is open and the shade doesn't need to be kept
2751                // open artificially.
2752                mStatusBarWindowManager.setHeadsUpShowing(false);
2753            } else {
2754                // we need to keep the panel open artificially, let's wait until the animation
2755                // is finished.
2756                mHeadsUpManager.setHeadsUpGoingAway(true);
2757                mStackScroller.runAfterAnimationFinished(new Runnable() {
2758                    @Override
2759                    public void run() {
2760                        if (!mHeadsUpManager.hasPinnedHeadsUp()) {
2761                            mStatusBarWindowManager.setHeadsUpShowing(false);
2762                            mHeadsUpManager.setHeadsUpGoingAway(false);
2763                        }
2764                        removeRemoteInputEntriesKeptUntilCollapsed();
2765                    }
2766                });
2767            }
2768        }
2769    }
2770
2771    @Override
2772    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
2773        dismissVolumeDialog();
2774    }
2775
2776    @Override
2777    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
2778    }
2779
2780    @Override
2781    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
2782        if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
2783            removeNotification(entry.key, mLatestRankingMap);
2784            mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2785            if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
2786                mLatestRankingMap = null;
2787            }
2788        } else {
2789            updateNotificationRanking(null);
2790            if (isHeadsUp) {
2791                mDozeServiceHost.fireNotificationHeadsUp();
2792            }
2793        }
2794
2795    }
2796
2797    protected void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2798            boolean alertAgain) {
2799        final boolean wasHeadsUp = isHeadsUp(key);
2800        if (wasHeadsUp) {
2801            if (!shouldPeek) {
2802                // We don't want this to be interrupting anymore, lets remove it
2803                mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */);
2804            } else {
2805                mHeadsUpManager.updateNotification(entry, alertAgain);
2806            }
2807        } else if (shouldPeek && alertAgain) {
2808            // This notification was updated to be a heads-up, show it!
2809            mHeadsUpManager.showNotification(entry);
2810        }
2811    }
2812
2813    protected void setHeadsUpUser(int newUserId) {
2814        if (mHeadsUpManager != null) {
2815            mHeadsUpManager.setUser(newUserId);
2816        }
2817    }
2818
2819    public boolean isHeadsUp(String key) {
2820        return mHeadsUpManager.isHeadsUp(key);
2821    }
2822
2823    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
2824        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
2825    }
2826
2827    public boolean isKeyguardCurrentlySecure() {
2828        return !mUnlockMethodCache.canSkipBouncer();
2829    }
2830
2831    public void setPanelExpanded(boolean isExpanded) {
2832        mPanelExpanded = isExpanded;
2833        updateHideIconsForBouncer(false /* animate */);
2834        mStatusBarWindowManager.setPanelExpanded(isExpanded);
2835        mVisualStabilityManager.setPanelExpanded(isExpanded);
2836        if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
2837            if (DEBUG) {
2838                Log.v(TAG, "clearing notification effects from setPanelExpanded");
2839            }
2840            clearNotificationEffects();
2841        }
2842
2843        if (!isExpanded) {
2844            removeRemoteInputEntriesKeptUntilCollapsed();
2845        }
2846    }
2847
2848    private void removeRemoteInputEntriesKeptUntilCollapsed() {
2849        for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
2850            Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
2851            mRemoteInputController.removeRemoteInput(entry, null);
2852            removeNotification(entry.key, mLatestRankingMap);
2853        }
2854        mRemoteInputEntriesToRemoveOnCollapse.clear();
2855    }
2856
2857    public NotificationStackScrollLayout getNotificationScrollLayout() {
2858        return mStackScroller;
2859    }
2860
2861    public boolean isPulsing() {
2862        return mDozeScrimController.isPulsing();
2863    }
2864
2865    @Override
2866    public void onReorderingAllowed() {
2867        updateNotifications();
2868    }
2869
2870    public boolean isLaunchTransitionFadingAway() {
2871        return mLaunchTransitionFadingAway;
2872    }
2873
2874    public boolean hideStatusBarIconsWhenExpanded() {
2875        return mNotificationPanel.hideStatusBarIconsWhenExpanded();
2876    }
2877
2878    @Override
2879    public void onColorsChanged(ColorExtractor extractor, int which) {
2880        updateTheme();
2881    }
2882
2883    public boolean isUsingDarkTheme() {
2884        OverlayInfo themeInfo = null;
2885        try {
2886            themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.dark",
2887                    mCurrentUserId);
2888        } catch (RemoteException e) {
2889            e.printStackTrace();
2890        }
2891        return themeInfo != null && themeInfo.isEnabled();
2892    }
2893
2894    @Nullable
2895    public View getAmbientIndicationContainer() {
2896        return mAmbientIndicationContainer;
2897    }
2898
2899    public void setOccluded(boolean occluded) {
2900        mIsOccluded = occluded;
2901        updateHideIconsForBouncer(false /* animate */);
2902    }
2903
2904    public boolean hideStatusBarIconsForBouncer() {
2905        return mHideIconsForBouncer || mWereIconsJustHidden;
2906    }
2907
2908    /**
2909     * @param animate should the change of the icons be animated.
2910     */
2911    private void updateHideIconsForBouncer(boolean animate) {
2912        boolean shouldHideIconsForBouncer = !mPanelExpanded && mTopHidesStatusBar && mIsOccluded
2913                && (mBouncerShowing || mStatusBarWindowHidden);
2914        if (mHideIconsForBouncer != shouldHideIconsForBouncer) {
2915            mHideIconsForBouncer = shouldHideIconsForBouncer;
2916            if (!shouldHideIconsForBouncer && mBouncerWasShowingWhenHidden) {
2917                // We're delaying the showing, since most of the time the fullscreen app will
2918                // hide the icons again and we don't want them to fade in and out immediately again.
2919                mWereIconsJustHidden = true;
2920                mHandler.postDelayed(() -> {
2921                    mWereIconsJustHidden = false;
2922                    recomputeDisableFlags(true);
2923                }, 500);
2924            } else {
2925                recomputeDisableFlags(animate);
2926            }
2927        }
2928        if (shouldHideIconsForBouncer) {
2929            mBouncerWasShowingWhenHidden = mBouncerShowing;
2930        }
2931    }
2932
2933    /**
2934     * All changes to the status bar and notifications funnel through here and are batched.
2935     */
2936    protected class H extends Handler {
2937        @Override
2938        public void handleMessage(Message m) {
2939            switch (m.what) {
2940                case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
2941                    toggleKeyboardShortcuts(m.arg1);
2942                    break;
2943                case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
2944                    dismissKeyboardShortcuts();
2945                    break;
2946                // End old BaseStatusBar.H handling.
2947                case MSG_OPEN_NOTIFICATION_PANEL:
2948                    animateExpandNotificationsPanel();
2949                    break;
2950                case MSG_OPEN_SETTINGS_PANEL:
2951                    animateExpandSettingsPanel((String) m.obj);
2952                    break;
2953                case MSG_CLOSE_PANELS:
2954                    animateCollapsePanels();
2955                    break;
2956                case MSG_LAUNCH_TRANSITION_TIMEOUT:
2957                    onLaunchTransitionTimeout();
2958                    break;
2959            }
2960        }
2961    }
2962
2963    public void maybeEscalateHeadsUp() {
2964        Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
2965        for (HeadsUpManager.HeadsUpEntry entry : entries) {
2966            final StatusBarNotification sbn = entry.entry.notification;
2967            final Notification notification = sbn.getNotification();
2968            if (notification.fullScreenIntent != null) {
2969                if (DEBUG) {
2970                    Log.d(TAG, "converting a heads up to fullScreen");
2971                }
2972                try {
2973                    EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
2974                            sbn.getKey());
2975                    notification.fullScreenIntent.send();
2976                    entry.entry.notifyFullScreenIntentLaunched();
2977                } catch (PendingIntent.CanceledException e) {
2978                }
2979            }
2980        }
2981        mHeadsUpManager.releaseAllImmediately();
2982    }
2983
2984    /**
2985     * Called for system navigation gestures. First action opens the panel, second opens
2986     * settings. Down action closes the entire panel.
2987     */
2988    @Override
2989    public void handleSystemKey(int key) {
2990        if (SPEW) Log.d(TAG, "handleNavigationKey: " + key);
2991        if (!panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive()
2992                || mKeyguardMonitor.isShowing() && !mKeyguardMonitor.isOccluded()) {
2993            return;
2994        }
2995
2996        // Panels are not available in setup
2997        if (!mUserSetup) return;
2998
2999        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
3000            mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
3001            mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
3002        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
3003            mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
3004            if (mNotificationPanel.isFullyCollapsed()) {
3005                mNotificationPanel.expand(true /* animate */);
3006                mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
3007            } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
3008                mNotificationPanel.flingSettings(0 /* velocity */, true /* expand */);
3009                mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
3010            }
3011        }
3012
3013    }
3014
3015    boolean panelsEnabled() {
3016        return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS;
3017    }
3018
3019    void makeExpandedVisible(boolean force) {
3020        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
3021        if (!force && (mExpandedVisible || !panelsEnabled())) {
3022            return;
3023        }
3024
3025        mExpandedVisible = true;
3026
3027        // Expand the window to encompass the full screen in anticipation of the drag.
3028        // This is only possible to do atomically because the status bar is at the top of the screen!
3029        mStatusBarWindowManager.setPanelVisible(true);
3030
3031        visibilityChanged(true);
3032        mWaitingForKeyguardExit = false;
3033        recomputeDisableFlags(!force /* animate */);
3034        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
3035    }
3036
3037    public void animateCollapsePanels() {
3038        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
3039    }
3040
3041    private final Runnable mAnimateCollapsePanels = new Runnable() {
3042        @Override
3043        public void run() {
3044            animateCollapsePanels();
3045        }
3046    };
3047
3048    public void postAnimateCollapsePanels() {
3049        mHandler.post(mAnimateCollapsePanels);
3050    }
3051
3052    public void postAnimateForceCollapsePanels() {
3053        mHandler.post(new Runnable() {
3054            @Override
3055            public void run() {
3056                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
3057            }
3058        });
3059    }
3060
3061    public void postAnimateOpenPanels() {
3062        mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
3063    }
3064
3065    @Override
3066    public void togglePanel() {
3067        if (mPanelExpanded) {
3068            animateCollapsePanels();
3069        } else {
3070            animateExpandNotificationsPanel();
3071        }
3072    }
3073
3074    @Override
3075    public void animateCollapsePanels(int flags) {
3076        animateCollapsePanels(flags, false /* force */, false /* delayed */,
3077                1.0f /* speedUpFactor */);
3078    }
3079
3080    public void animateCollapsePanels(int flags, boolean force) {
3081        animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
3082    }
3083
3084    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
3085        animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
3086    }
3087
3088    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
3089            float speedUpFactor) {
3090        if (!force && mState != StatusBarState.SHADE) {
3091            runPostCollapseRunnables();
3092            return;
3093        }
3094        if (SPEW) {
3095            Log.d(TAG, "animateCollapse():"
3096                    + " mExpandedVisible=" + mExpandedVisible
3097                    + " flags=" + flags);
3098        }
3099
3100        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
3101            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
3102                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
3103                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
3104            }
3105        }
3106
3107        if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) {
3108            // release focus immediately to kick off focus change transition
3109            mStatusBarWindowManager.setStatusBarFocusable(false);
3110
3111            mStatusBarWindow.cancelExpandHelper();
3112            mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
3113        }
3114    }
3115
3116    private void runPostCollapseRunnables() {
3117        ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables);
3118        mPostCollapseRunnables.clear();
3119        int size = clonedList.size();
3120        for (int i = 0; i < size; i++) {
3121            clonedList.get(i).run();
3122        }
3123        mStatusBarKeyguardViewManager.readyForKeyguardDone();
3124    }
3125
3126    @Override
3127    public void animateExpandNotificationsPanel() {
3128        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
3129        if (!panelsEnabled()) {
3130            return ;
3131        }
3132
3133        mNotificationPanel.expand(true /* animate */);
3134
3135        if (false) postStartTracing();
3136    }
3137
3138    @Override
3139    public void animateExpandSettingsPanel(String subPanel) {
3140        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
3141        if (!panelsEnabled()) {
3142            return;
3143        }
3144
3145        // Settings are not available in setup
3146        if (!mUserSetup) return;
3147
3148
3149        if (subPanel != null) {
3150            mQSPanel.openDetails(subPanel);
3151        }
3152        mNotificationPanel.expandWithQs();
3153
3154        if (false) postStartTracing();
3155    }
3156
3157    public void animateCollapseQuickSettings() {
3158        if (mState == StatusBarState.SHADE) {
3159            mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
3160        }
3161    }
3162
3163    void makeExpandedInvisible() {
3164        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
3165                + " mExpandedVisible=" + mExpandedVisible);
3166
3167        if (!mExpandedVisible || mStatusBarWindow == null) {
3168            return;
3169        }
3170
3171        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
3172        mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
3173                1.0f /* speedUpFactor */);
3174
3175        mNotificationPanel.closeQs();
3176
3177        mExpandedVisible = false;
3178        visibilityChanged(false);
3179
3180        // Shrink the window to the size of the status bar only
3181        mStatusBarWindowManager.setPanelVisible(false);
3182        mStatusBarWindowManager.setForceStatusBarVisible(false);
3183
3184        // Close any guts that might be visible
3185        closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,
3186                -1 /* x */, -1 /* y */, true /* resetMenu */);
3187
3188        runPostCollapseRunnables();
3189        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
3190        showBouncerIfKeyguard();
3191        recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
3192
3193        // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
3194        // the bouncer appear animation.
3195        if (!mStatusBarKeyguardViewManager.isShowing()) {
3196            WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
3197        }
3198    }
3199
3200    public boolean interceptTouchEvent(MotionEvent event) {
3201        if (DEBUG_GESTURES) {
3202            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
3203                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
3204                        event.getActionMasked(), (int) event.getX(), (int) event.getY(),
3205                        mDisabled1, mDisabled2);
3206            }
3207
3208        }
3209
3210        if (SPEW) {
3211            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
3212                + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking);
3213        } else if (CHATTY) {
3214            if (event.getAction() != MotionEvent.ACTION_MOVE) {
3215                Log.d(TAG, String.format(
3216                            "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x",
3217                            MotionEvent.actionToString(event.getAction()),
3218                            event.getRawX(), event.getRawY(), mDisabled1, mDisabled2));
3219            }
3220        }
3221
3222        if (DEBUG_GESTURES) {
3223            mGestureRec.add(event);
3224        }
3225
3226        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
3227            final boolean upOrCancel =
3228                    event.getAction() == MotionEvent.ACTION_UP ||
3229                    event.getAction() == MotionEvent.ACTION_CANCEL;
3230            if (upOrCancel && !mExpandedVisible) {
3231                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
3232            } else {
3233                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
3234            }
3235        }
3236        return false;
3237    }
3238
3239    public GestureRecorder getGestureRecorder() {
3240        return mGestureRec;
3241    }
3242
3243    public FingerprintUnlockController getFingerprintUnlockController() {
3244        return mFingerprintUnlockController;
3245    }
3246
3247    @Override // CommandQueue
3248    public void setWindowState(int window, int state) {
3249        boolean showing = state == WINDOW_STATE_SHOWING;
3250        if (mStatusBarWindow != null
3251                && window == StatusBarManager.WINDOW_STATUS_BAR
3252                && mStatusBarWindowState != state) {
3253            mStatusBarWindowState = state;
3254            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
3255            if (!showing && mState == StatusBarState.SHADE) {
3256                mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
3257                        1.0f /* speedUpFactor */);
3258            }
3259            if (mStatusBarView != null) {
3260                mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
3261                updateHideIconsForBouncer(false /* animate */);
3262            }
3263        }
3264    }
3265
3266    @Override // CommandQueue
3267    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
3268            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
3269        final int oldVal = mSystemUiVisibility;
3270        final int newVal = (oldVal&~mask) | (vis&mask);
3271        final int diff = newVal ^ oldVal;
3272        if (DEBUG) Log.d(TAG, String.format(
3273                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
3274                Integer.toHexString(vis), Integer.toHexString(mask),
3275                Integer.toHexString(oldVal), Integer.toHexString(newVal),
3276                Integer.toHexString(diff)));
3277        boolean sbModeChanged = false;
3278        if (diff != 0) {
3279            mSystemUiVisibility = newVal;
3280
3281            // update low profile
3282            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
3283                setAreThereNotifications();
3284            }
3285
3286            // ready to unhide
3287            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
3288                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
3289                mNoAnimationOnNextBarModeChange = true;
3290            }
3291
3292            // update status bar mode
3293            final int sbMode = computeStatusBarMode(oldVal, newVal);
3294
3295            sbModeChanged = sbMode != -1;
3296            if (sbModeChanged && sbMode != mStatusBarMode) {
3297                if (sbMode != mStatusBarMode) {
3298                    mStatusBarMode = sbMode;
3299                    checkBarModes();
3300                }
3301                touchAutoHide();
3302            }
3303
3304            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
3305                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
3306            }
3307
3308            // send updated sysui visibility to window manager
3309            notifyUiVisibilityChanged(mSystemUiVisibility);
3310        }
3311
3312        mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
3313                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
3314    }
3315
3316    void touchAutoHide() {
3317        // update transient bar autohide
3318        if (mStatusBarMode == MODE_SEMI_TRANSPARENT || (mNavigationBar != null
3319                && mNavigationBar.isSemiTransparent())) {
3320            scheduleAutohide();
3321        } else {
3322            cancelAutohide();
3323        }
3324    }
3325
3326    protected int computeStatusBarMode(int oldVal, int newVal) {
3327        return computeBarMode(oldVal, newVal, View.STATUS_BAR_TRANSIENT,
3328                View.STATUS_BAR_TRANSLUCENT, View.STATUS_BAR_TRANSPARENT);
3329    }
3330
3331    protected BarTransitions getStatusBarTransitions() {
3332        return mStatusBarView.getBarTransitions();
3333    }
3334
3335    protected int computeBarMode(int oldVis, int newVis,
3336            int transientFlag, int translucentFlag, int transparentFlag) {
3337        final int oldMode = barMode(oldVis, transientFlag, translucentFlag, transparentFlag);
3338        final int newMode = barMode(newVis, transientFlag, translucentFlag, transparentFlag);
3339        if (oldMode == newMode) {
3340            return -1; // no mode change
3341        }
3342        return newMode;
3343    }
3344
3345    private int barMode(int vis, int transientFlag, int translucentFlag, int transparentFlag) {
3346        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | transparentFlag;
3347        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
3348                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
3349                : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
3350                : (vis & transparentFlag) != 0 ? MODE_TRANSPARENT
3351                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
3352                : MODE_OPAQUE;
3353    }
3354
3355    void checkBarModes() {
3356        if (mDemoMode) return;
3357        if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
3358                getStatusBarTransitions());
3359        if (mNavigationBar != null) mNavigationBar.checkNavBarModes();
3360        mNoAnimationOnNextBarModeChange = false;
3361    }
3362
3363    // Called by NavigationBarFragment
3364    void setQsScrimEnabled(boolean scrimEnabled) {
3365        mNotificationPanel.setQsScrimEnabled(scrimEnabled);
3366    }
3367
3368    void checkBarMode(int mode, int windowState, BarTransitions transitions) {
3369        final boolean powerSave = mBatteryController.isPowerSave();
3370        final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
3371                && windowState != WINDOW_STATE_HIDDEN && !powerSave;
3372        if (powerSave && getBarState() == StatusBarState.SHADE) {
3373            mode = MODE_WARNING;
3374        }
3375        transitions.transitionTo(mode, anim);
3376    }
3377
3378    private void finishBarAnimations() {
3379        if (mStatusBarView != null) {
3380            mStatusBarView.getBarTransitions().finishAnimations();
3381        }
3382        if (mNavigationBar != null) {
3383            mNavigationBar.finishBarAnimations();
3384        }
3385    }
3386
3387    private final Runnable mCheckBarModes = new Runnable() {
3388        @Override
3389        public void run() {
3390            checkBarModes();
3391        }
3392    };
3393
3394    public void setInteracting(int barWindow, boolean interacting) {
3395        final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
3396        mInteractingWindows = interacting
3397                ? (mInteractingWindows | barWindow)
3398                : (mInteractingWindows & ~barWindow);
3399        if (mInteractingWindows != 0) {
3400            suspendAutohide();
3401        } else {
3402            resumeSuspendedAutohide();
3403        }
3404        // manually dismiss the volume panel when interacting with the nav bar
3405        if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
3406            touchAutoDim();
3407            dismissVolumeDialog();
3408        }
3409        checkBarModes();
3410    }
3411
3412    private void dismissVolumeDialog() {
3413        if (mVolumeComponent != null) {
3414            mVolumeComponent.dismissNow();
3415        }
3416    }
3417
3418    private void resumeSuspendedAutohide() {
3419        if (mAutohideSuspended) {
3420            scheduleAutohide();
3421            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
3422        }
3423    }
3424
3425    private void suspendAutohide() {
3426        mHandler.removeCallbacks(mAutohide);
3427        mHandler.removeCallbacks(mCheckBarModes);
3428        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
3429    }
3430
3431    private void cancelAutohide() {
3432        mAutohideSuspended = false;
3433        mHandler.removeCallbacks(mAutohide);
3434    }
3435
3436    private void scheduleAutohide() {
3437        cancelAutohide();
3438        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
3439    }
3440
3441    public void touchAutoDim() {
3442        if (mNavigationBar != null) {
3443            mNavigationBar.getBarTransitions().setAutoDim(false);
3444        }
3445        mHandler.removeCallbacks(mAutoDim);
3446        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
3447            mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
3448        }
3449    }
3450
3451    void checkUserAutohide(View v, MotionEvent event) {
3452        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
3453                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
3454                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
3455                && !mRemoteInputController.isRemoteInputActive()) { // not due to typing in IME
3456            userAutohide();
3457        }
3458    }
3459
3460    private void checkRemoteInputOutside(MotionEvent event) {
3461        if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
3462                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
3463                && mRemoteInputController.isRemoteInputActive()) {
3464            mRemoteInputController.closeRemoteInputs();
3465        }
3466    }
3467
3468    private void userAutohide() {
3469        cancelAutohide();
3470        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
3471    }
3472
3473    private boolean areLightsOn() {
3474        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
3475    }
3476
3477    public void setLightsOn(boolean on) {
3478        Log.v(TAG, "setLightsOn(" + on + ")");
3479        if (on) {
3480            setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
3481                    mLastFullscreenStackBounds, mLastDockedStackBounds);
3482        } else {
3483            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
3484                    View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
3485                    mLastDockedStackBounds);
3486        }
3487    }
3488
3489    private void notifyUiVisibilityChanged(int vis) {
3490        try {
3491            if (mLastDispatchedSystemUiVisibility != vis) {
3492                mWindowManagerService.statusBarVisibilityChanged(vis);
3493                mLastDispatchedSystemUiVisibility = vis;
3494            }
3495        } catch (RemoteException ex) {
3496        }
3497    }
3498
3499    @Override
3500    public void topAppWindowChanged(boolean showMenu) {
3501        if (SPEW) {
3502            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
3503        }
3504
3505        // See above re: lights-out policy for legacy apps.
3506        if (showMenu) setLightsOn(true);
3507    }
3508
3509    public static String viewInfo(View v) {
3510        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
3511                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
3512    }
3513
3514    @Override
3515    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3516        synchronized (mQueueLock) {
3517            pw.println("Current Status Bar state:");
3518            pw.println("  mExpandedVisible=" + mExpandedVisible
3519                    + ", mTrackingPosition=" + mTrackingPosition);
3520            pw.println("  mTracking=" + mTracking);
3521            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
3522            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
3523            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
3524                    + " scroll " + mStackScroller.getScrollX()
3525                    + "," + mStackScroller.getScrollY());
3526        }
3527        pw.print("  mPendingNotifications=");
3528        if (mPendingNotifications.size() == 0) {
3529            pw.println("null");
3530        } else {
3531            for (Entry entry : mPendingNotifications.values()) {
3532                pw.println(entry.notification);
3533            }
3534        }
3535
3536        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
3537        pw.print("  mStatusBarWindowState=");
3538        pw.println(windowStateToString(mStatusBarWindowState));
3539        pw.print("  mStatusBarMode=");
3540        pw.println(BarTransitions.modeToString(mStatusBarMode));
3541        pw.print("  mDozing="); pw.println(mDozing);
3542        pw.print("  mZenMode=");
3543        pw.println(Settings.Global.zenModeToString(mZenMode));
3544        pw.print("  mUseHeadsUp=");
3545        pw.println(mUseHeadsUp);
3546        pw.print("  mKeyToRemoveOnGutsClosed=");
3547        pw.println(mKeyToRemoveOnGutsClosed);
3548        if (mStatusBarView != null) {
3549            dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
3550        }
3551
3552        pw.print("  mMediaSessionManager=");
3553        pw.println(mMediaSessionManager);
3554        pw.print("  mMediaNotificationKey=");
3555        pw.println(mMediaNotificationKey);
3556        pw.print("  mMediaController=");
3557        pw.print(mMediaController);
3558        if (mMediaController != null) {
3559            pw.print(" state=" + mMediaController.getPlaybackState());
3560        }
3561        pw.println();
3562        pw.print("  mMediaMetadata=");
3563        pw.print(mMediaMetadata);
3564        if (mMediaMetadata != null) {
3565            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
3566        }
3567        pw.println();
3568
3569        pw.println("  Panels: ");
3570        if (mNotificationPanel != null) {
3571            pw.println("    mNotificationPanel=" +
3572                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
3573            pw.print  ("      ");
3574            mNotificationPanel.dump(fd, pw, args);
3575        }
3576        pw.println("  mStackScroller: ");
3577        if (mStackScroller != null) {
3578            pw.print  ("      ");
3579            mStackScroller.dump(fd, pw, args);
3580        }
3581        pw.println("  Theme:");
3582        if (mOverlayManager == null) {
3583            pw.println("    overlay manager not initialized!");
3584        } else {
3585            pw.println("    dark overlay on: " + isUsingDarkTheme());
3586        }
3587        final boolean lightWpTheme = mContext.getThemeResId() == R.style.Theme_SystemUI_Light;
3588        pw.println("    light wallpaper theme: " + lightWpTheme);
3589
3590        DozeLog.dump(pw);
3591
3592        if (mFingerprintUnlockController != null) {
3593            mFingerprintUnlockController.dump(pw);
3594        }
3595
3596        if (mScrimController != null) {
3597            mScrimController.dump(pw);
3598        }
3599
3600        if (DUMPTRUCK) {
3601            synchronized (mNotificationData) {
3602                mNotificationData.dump(pw, "  ");
3603            }
3604
3605            if (false) {
3606                pw.println("see the logcat for a dump of the views we have created.");
3607                // must happen on ui thread
3608                mHandler.post(new Runnable() {
3609                        @Override
3610                        public void run() {
3611                            mStatusBarView.getLocationOnScreen(mAbsPos);
3612                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
3613                                    + ") " + mStatusBarView.getWidth() + "x"
3614                                    + getStatusBarHeight());
3615                            mStatusBarView.debug();
3616                        }
3617                    });
3618            }
3619        }
3620
3621        if (DEBUG_GESTURES) {
3622            pw.print("  status bar gestures: ");
3623            mGestureRec.dump(fd, pw, args);
3624        }
3625
3626        if (mHeadsUpManager != null) {
3627            mHeadsUpManager.dump(fd, pw, args);
3628        } else {
3629            pw.println("  mHeadsUpManager: null");
3630        }
3631        if (mGroupManager != null) {
3632            mGroupManager.dump(fd, pw, args);
3633        } else {
3634            pw.println("  mGroupManager: null");
3635        }
3636
3637        if (mLightBarController != null) {
3638            mLightBarController.dump(fd, pw, args);
3639        }
3640
3641        if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
3642            KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
3643        }
3644
3645        FalsingManager.getInstance(mContext).dump(pw);
3646        FalsingLog.dump(pw);
3647
3648        pw.println("SharedPreferences:");
3649        for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
3650            pw.print("  "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
3651        }
3652    }
3653
3654    static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
3655        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
3656        pw.println(BarTransitions.modeToString(transitions.getMode()));
3657    }
3658
3659    public void createAndAddWindows() {
3660        addStatusBarWindow();
3661    }
3662
3663    private void addStatusBarWindow() {
3664        makeStatusBarView();
3665        mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
3666        mRemoteInputController = new RemoteInputController(mHeadsUpManager);
3667        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
3668    }
3669
3670    // called by makeStatusbar and also by PhoneStatusBarView
3671    void updateDisplaySize() {
3672        mDisplay.getMetrics(mDisplayMetrics);
3673        mDisplay.getSize(mCurrentDisplaySize);
3674        if (DEBUG_GESTURES) {
3675            mGestureRec.tag("display",
3676                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
3677        }
3678    }
3679
3680    float getDisplayDensity() {
3681        return mDisplayMetrics.density;
3682    }
3683
3684    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
3685            boolean dismissShade) {
3686        startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
3687                false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */);
3688    }
3689
3690    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
3691            final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
3692            final Callback callback) {
3693        if (onlyProvisioned && !isDeviceProvisioned()) return;
3694
3695        final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
3696                mContext, intent, mCurrentUserId);
3697        Runnable runnable = new Runnable() {
3698            @Override
3699            public void run() {
3700                mAssistManager.hideAssist();
3701                intent.setFlags(
3702                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
3703                int result = ActivityManager.START_CANCELED;
3704                ActivityOptions options = new ActivityOptions(getActivityOptions());
3705                options.setDisallowEnterPictureInPictureWhileLaunching(
3706                        disallowEnterPictureInPictureWhileLaunching);
3707                if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
3708                    // Normally an activity will set it's requested rotation
3709                    // animation on its window. However when launching an activity
3710                    // causes the orientation to change this is too late. In these cases
3711                    // the default animation is used. This doesn't look good for
3712                    // the camera (as it rotates the camera contents out of sync
3713                    // with physical reality). So, we ask the WindowManager to
3714                    // force the crossfade animation if an orientation change
3715                    // happens to occur during the launch.
3716                    options.setRotationAnimationHint(
3717                            WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
3718                }
3719                try {
3720                    result = ActivityManager.getService().startActivityAsUser(
3721                            null, mContext.getBasePackageName(),
3722                            intent,
3723                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
3724                            null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
3725                            options.toBundle(), UserHandle.CURRENT.getIdentifier());
3726                } catch (RemoteException e) {
3727                    Log.w(TAG, "Unable to start activity", e);
3728                }
3729                if (callback != null) {
3730                    callback.onActivityStarted(result);
3731                }
3732            }
3733        };
3734        Runnable cancelRunnable = new Runnable() {
3735            @Override
3736            public void run() {
3737                if (callback != null) {
3738                    callback.onActivityStarted(ActivityManager.START_CANCELED);
3739                }
3740            }
3741        };
3742        executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
3743                afterKeyguardGone, true /* deferred */);
3744    }
3745
3746    public void readyForKeyguardDone() {
3747        mStatusBarKeyguardViewManager.readyForKeyguardDone();
3748    }
3749
3750    public void executeRunnableDismissingKeyguard(final Runnable runnable,
3751            final Runnable cancelAction,
3752            final boolean dismissShade,
3753            final boolean afterKeyguardGone,
3754            final boolean deferred) {
3755        dismissKeyguardThenExecute(() -> {
3756            if (runnable != null) {
3757                if (mStatusBarKeyguardViewManager.isShowing()
3758                        && mStatusBarKeyguardViewManager.isOccluded()) {
3759                    mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
3760                } else {
3761                    AsyncTask.execute(runnable);
3762                }
3763            }
3764            if (dismissShade) {
3765                if (mExpandedVisible) {
3766                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
3767                            true /* delayed*/);
3768                } else {
3769
3770                    // Do it after DismissAction has been processed to conserve the needed ordering.
3771                    mHandler.post(this::runPostCollapseRunnables);
3772                }
3773            } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) {
3774
3775                // We are not dismissing the shade, but the launch transition is already finished,
3776                // so nobody will call readyForKeyguardDone anymore. Post it such that
3777                // keyguardDonePending gets called first.
3778                mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
3779            }
3780            return deferred;
3781        }, cancelAction, afterKeyguardGone);
3782    }
3783
3784    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
3785        @Override
3786        public void onReceive(Context context, Intent intent) {
3787            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3788            String action = intent.getAction();
3789            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
3790                KeyboardShortcuts.dismiss();
3791                if (mRemoteInputController != null) {
3792                    mRemoteInputController.closeRemoteInputs();
3793                }
3794                if (isCurrentProfile(getSendingUserId())) {
3795                    int flags = CommandQueue.FLAG_EXCLUDE_NONE;
3796                    String reason = intent.getStringExtra("reason");
3797                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
3798                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
3799                    }
3800                    animateCollapsePanels(flags);
3801                }
3802            }
3803            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
3804                finishBarAnimations();
3805                resetUserExpandedStates();
3806            }
3807            else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
3808                mQSPanel.showDeviceMonitoringDialog();
3809            }
3810        }
3811    };
3812
3813    private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() {
3814        @Override
3815        public void onReceive(Context context, Intent intent) {
3816            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3817            String action = intent.getAction();
3818            if (ACTION_DEMO.equals(action)) {
3819                Bundle bundle = intent.getExtras();
3820                if (bundle != null) {
3821                    String command = bundle.getString("command", "").trim().toLowerCase();
3822                    if (command.length() > 0) {
3823                        try {
3824                            dispatchDemoCommand(command, bundle);
3825                        } catch (Throwable t) {
3826                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
3827                        }
3828                    }
3829                }
3830            } else if (ACTION_FAKE_ARTWORK.equals(action)) {
3831                if (DEBUG_MEDIA_FAKE_ARTWORK) {
3832                    updateMediaMetaData(true, true);
3833                }
3834            }
3835        }
3836    };
3837
3838    public void resetUserExpandedStates() {
3839        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3840        final int notificationCount = activeNotifications.size();
3841        for (int i = 0; i < notificationCount; i++) {
3842            NotificationData.Entry entry = activeNotifications.get(i);
3843            if (entry.row != null) {
3844                entry.row.resetUserExpansion();
3845            }
3846        }
3847    }
3848
3849    protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
3850        dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
3851    }
3852
3853    private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
3854            boolean afterKeyguardGone) {
3855        if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
3856                && mUnlockMethodCache.canSkipBouncer()
3857                && !mLeaveOpenOnKeyguardHide
3858                && isPulsing()) {
3859            // Reuse the fingerprint wake-and-unlock transition if we dismiss keyguard from a pulse.
3860            // TODO: Factor this transition out of FingerprintUnlockController.
3861            mFingerprintUnlockController.startWakeAndUnlock(
3862                    FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
3863        }
3864        if (mStatusBarKeyguardViewManager.isShowing()) {
3865            mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
3866                    afterKeyguardGone);
3867        } else {
3868            action.onDismiss();
3869        }
3870    }
3871
3872    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
3873    @Override
3874    public void onConfigChanged(Configuration newConfig) {
3875        updateResources();
3876        updateDisplaySize(); // populates mDisplayMetrics
3877
3878        if (DEBUG) {
3879            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
3880        }
3881
3882        updateRowStates();
3883        mScreenPinningRequest.onConfigurationChanged();
3884    }
3885
3886    public void userSwitched(int newUserId) {
3887        // Begin old BaseStatusBar.userSwitched
3888        setHeadsUpUser(newUserId);
3889        // End old BaseStatusBar.userSwitched
3890        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
3891        animateCollapsePanels();
3892        updatePublicMode();
3893        mNotificationData.filterAndSort();
3894        if (mReinflateNotificationsOnUserSwitched) {
3895            updateNotificationsOnDensityOrFontScaleChanged();
3896            mReinflateNotificationsOnUserSwitched = false;
3897        }
3898        updateNotificationShade();
3899        clearCurrentMediaNotification();
3900        setLockscreenUser(newUserId);
3901    }
3902
3903    protected void setLockscreenUser(int newUserId) {
3904        mLockscreenWallpaper.setCurrentUser(newUserId);
3905        mScrimController.setCurrentUser(newUserId);
3906        updateMediaMetaData(true, false);
3907    }
3908
3909    /**
3910     * Reload some of our resources when the configuration changes.
3911     *
3912     * We don't reload everything when the configuration changes -- we probably
3913     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
3914     * meantime, just update the things that we know change.
3915     */
3916    void updateResources() {
3917        // Update the quick setting tiles
3918        if (mQSPanel != null) {
3919            mQSPanel.updateResources();
3920        }
3921
3922        loadDimens();
3923
3924        if (mNotificationPanel != null) {
3925            mNotificationPanel.updateResources();
3926        }
3927        if (mBrightnessMirrorController != null) {
3928            mBrightnessMirrorController.updateResources();
3929        }
3930    }
3931
3932    protected void loadDimens() {
3933        final Resources res = mContext.getResources();
3934
3935        int oldBarHeight = mNaturalBarHeight;
3936        mNaturalBarHeight = res.getDimensionPixelSize(
3937                com.android.internal.R.dimen.status_bar_height);
3938        if (mStatusBarWindowManager != null && mNaturalBarHeight != oldBarHeight) {
3939            mStatusBarWindowManager.setBarHeight(mNaturalBarHeight);
3940        }
3941        mMaxAllowedKeyguardNotifications = res.getInteger(
3942                R.integer.keyguard_max_notification_count);
3943
3944        if (DEBUG) Log.v(TAG, "defineSlots");
3945    }
3946
3947    // Visibility reporting
3948
3949    protected void handleVisibleToUserChanged(boolean visibleToUser) {
3950        if (visibleToUser) {
3951            handleVisibleToUserChangedImpl(visibleToUser);
3952            startNotificationLogging();
3953        } else {
3954            stopNotificationLogging();
3955            handleVisibleToUserChangedImpl(visibleToUser);
3956        }
3957    }
3958
3959    void handlePeekToExpandTransistion() {
3960        try {
3961            // consider the transition from peek to expanded to be a panel open,
3962            // but not one that clears notification effects.
3963            int notificationLoad = mNotificationData.getActiveNotifications().size();
3964            mBarService.onPanelRevealed(false, notificationLoad);
3965        } catch (RemoteException ex) {
3966            // Won't fail unless the world has ended.
3967        }
3968    }
3969
3970    /**
3971     * The LEDs are turned off when the notification panel is shown, even just a little bit.
3972     * See also StatusBar.setPanelExpanded for another place where we attempt to do this.
3973     */
3974    // Old BaseStatusBar.handleVisibileToUserChanged
3975    private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
3976        try {
3977            if (visibleToUser) {
3978                boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
3979                boolean clearNotificationEffects =
3980                        !isPanelFullyCollapsed() &&
3981                        (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
3982                int notificationLoad = mNotificationData.getActiveNotifications().size();
3983                if (pinnedHeadsUp && isPanelFullyCollapsed())  {
3984                    notificationLoad = 1;
3985                }
3986                mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
3987            } else {
3988                mBarService.onPanelHidden();
3989            }
3990        } catch (RemoteException ex) {
3991            // Won't fail unless the world has ended.
3992        }
3993    }
3994
3995    private void stopNotificationLogging() {
3996        // Report all notifications as invisible and turn down the
3997        // reporter.
3998        if (!mCurrentlyVisibleNotifications.isEmpty()) {
3999            logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(),
4000                    mCurrentlyVisibleNotifications);
4001            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
4002        }
4003        mHandler.removeCallbacks(mVisibilityReporter);
4004        mStackScroller.setChildLocationsChangedListener(null);
4005    }
4006
4007    private void startNotificationLogging() {
4008        mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
4009        // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
4010        // cause the scroller to emit child location events. Hence generate
4011        // one ourselves to guarantee that we're reporting visible
4012        // notifications.
4013        // (Note that in cases where the scroller does emit events, this
4014        // additional event doesn't break anything.)
4015        mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
4016    }
4017
4018    private void logNotificationVisibilityChanges(
4019            Collection<NotificationVisibility> newlyVisible,
4020            Collection<NotificationVisibility> noLongerVisible) {
4021        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
4022            return;
4023        }
4024        NotificationVisibility[] newlyVisibleAr =
4025                newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]);
4026        NotificationVisibility[] noLongerVisibleAr =
4027                noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]);
4028        try {
4029            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
4030        } catch (RemoteException e) {
4031            // Ignore.
4032        }
4033
4034        final int N = newlyVisible.size();
4035        if (N > 0) {
4036            String[] newlyVisibleKeyAr = new String[N];
4037            for (int i = 0; i < N; i++) {
4038                newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
4039            }
4040
4041            setNotificationsShown(newlyVisibleKeyAr);
4042        }
4043    }
4044
4045    // State logging
4046
4047    private void logStateToEventlog() {
4048        boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
4049        boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
4050        boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
4051        boolean isSecure = mUnlockMethodCache.isMethodSecure();
4052        boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer();
4053        int stateFingerprint = getLoggingFingerprint(mState,
4054                isShowing,
4055                isOccluded,
4056                isBouncerShowing,
4057                isSecure,
4058                canSkipBouncer);
4059        if (stateFingerprint != mLastLoggedStateFingerprint) {
4060            if (mStatusBarStateLog == null) {
4061                mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
4062            }
4063            mMetricsLogger.write(mStatusBarStateLog
4064                    .setCategory(isBouncerShowing ? MetricsEvent.BOUNCER : MetricsEvent.LOCKSCREEN)
4065                    .setType(isShowing ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
4066                    .setSubtype(isSecure ? 1 : 0));
4067            EventLogTags.writeSysuiStatusBarState(mState,
4068                    isShowing ? 1 : 0,
4069                    isOccluded ? 1 : 0,
4070                    isBouncerShowing ? 1 : 0,
4071                    isSecure ? 1 : 0,
4072                    canSkipBouncer ? 1 : 0);
4073            mLastLoggedStateFingerprint = stateFingerprint;
4074        }
4075    }
4076
4077    /**
4078     * Returns a fingerprint of fields logged to eventlog
4079     */
4080    private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing,
4081            boolean keyguardOccluded, boolean bouncerShowing, boolean secure,
4082            boolean currentlyInsecure) {
4083        // Reserve 8 bits for statusBarState. We'll never go higher than
4084        // that, right? Riiiight.
4085        return (statusBarState & 0xFF)
4086                | ((keyguardShowing   ? 1 : 0) <<  8)
4087                | ((keyguardOccluded  ? 1 : 0) <<  9)
4088                | ((bouncerShowing    ? 1 : 0) << 10)
4089                | ((secure            ? 1 : 0) << 11)
4090                | ((currentlyInsecure ? 1 : 0) << 12);
4091    }
4092
4093    //
4094    // tracing
4095    //
4096
4097    void postStartTracing() {
4098        mHandler.postDelayed(mStartTracing, 3000);
4099    }
4100
4101    void vibrate() {
4102        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
4103                Context.VIBRATOR_SERVICE);
4104        vib.vibrate(250, VIBRATION_ATTRIBUTES);
4105    }
4106
4107    Runnable mStartTracing = new Runnable() {
4108        @Override
4109        public void run() {
4110            vibrate();
4111            SystemClock.sleep(250);
4112            Log.d(TAG, "startTracing");
4113            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
4114            mHandler.postDelayed(mStopTracing, 10000);
4115        }
4116    };
4117
4118    Runnable mStopTracing = new Runnable() {
4119        @Override
4120        public void run() {
4121            android.os.Debug.stopMethodTracing();
4122            Log.d(TAG, "stopTracing");
4123            vibrate();
4124        }
4125    };
4126
4127    @Override
4128    public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
4129        mHandler.post(() -> {
4130            mLeaveOpenOnKeyguardHide = true;
4131            executeRunnableDismissingKeyguard(() -> mHandler.post(runnable), null, false, false,
4132                    false);
4133        });
4134    }
4135
4136    @Override
4137    public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
4138        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent));
4139    }
4140
4141    @Override
4142    public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
4143        mHandler.postDelayed(() ->
4144                handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/), delay);
4145    }
4146
4147    private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
4148        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
4149    }
4150
4151    private static class FastColorDrawable extends Drawable {
4152        private final int mColor;
4153
4154        public FastColorDrawable(int color) {
4155            mColor = 0xff000000 | color;
4156        }
4157
4158        @Override
4159        public void draw(Canvas canvas) {
4160            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
4161        }
4162
4163        @Override
4164        public void setAlpha(int alpha) {
4165        }
4166
4167        @Override
4168        public void setColorFilter(ColorFilter colorFilter) {
4169        }
4170
4171        @Override
4172        public int getOpacity() {
4173            return PixelFormat.OPAQUE;
4174        }
4175
4176        @Override
4177        public void setBounds(int left, int top, int right, int bottom) {
4178        }
4179
4180        @Override
4181        public void setBounds(Rect bounds) {
4182        }
4183    }
4184
4185    public void destroy() {
4186        // Begin old BaseStatusBar.destroy().
4187        mContext.unregisterReceiver(mBaseBroadcastReceiver);
4188        try {
4189            mNotificationListener.unregisterAsSystemService();
4190        } catch (RemoteException e) {
4191            // Ignore.
4192        }
4193        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
4194        // End old BaseStatusBar.destroy().
4195        if (mStatusBarWindow != null) {
4196            mWindowManager.removeViewImmediate(mStatusBarWindow);
4197            mStatusBarWindow = null;
4198        }
4199        if (mNavigationBarView != null) {
4200            mWindowManager.removeViewImmediate(mNavigationBarView);
4201            mNavigationBarView = null;
4202        }
4203        mContext.unregisterReceiver(mBroadcastReceiver);
4204        mContext.unregisterReceiver(mDemoReceiver);
4205        mAssistManager.destroy();
4206
4207        if (mQSPanel != null && mQSPanel.getHost() != null) {
4208            mQSPanel.getHost().destroy();
4209        }
4210        Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null);
4211        mDeviceProvisionedController.removeCallback(mUserSetupObserver);
4212        Dependency.get(ConfigurationController.class).removeCallback(this);
4213    }
4214
4215    private boolean mDemoModeAllowed;
4216    private boolean mDemoMode;
4217
4218    @Override
4219    public void dispatchDemoCommand(String command, Bundle args) {
4220        if (!mDemoModeAllowed) {
4221            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
4222                    DEMO_MODE_ALLOWED, 0) != 0;
4223        }
4224        if (!mDemoModeAllowed) return;
4225        if (command.equals(COMMAND_ENTER)) {
4226            mDemoMode = true;
4227        } else if (command.equals(COMMAND_EXIT)) {
4228            mDemoMode = false;
4229            checkBarModes();
4230        } else if (!mDemoMode) {
4231            // automatically enter demo mode on first demo command
4232            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
4233        }
4234        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
4235        if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) {
4236            mVolumeComponent.dispatchDemoCommand(command, args);
4237        }
4238        if (modeChange || command.equals(COMMAND_CLOCK)) {
4239            dispatchDemoCommandToView(command, args, R.id.clock);
4240        }
4241        if (modeChange || command.equals(COMMAND_BATTERY)) {
4242            mBatteryController.dispatchDemoCommand(command, args);
4243        }
4244        if (modeChange || command.equals(COMMAND_STATUS)) {
4245            ((StatusBarIconControllerImpl) mIconController).dispatchDemoCommand(command, args);
4246        }
4247        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
4248            mNetworkController.dispatchDemoCommand(command, args);
4249        }
4250        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
4251            View notifications = mStatusBarView == null ? null
4252                    : mStatusBarView.findViewById(R.id.notification_icon_area);
4253            if (notifications != null) {
4254                String visible = args.getString("visible");
4255                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
4256                notifications.setVisibility(vis);
4257            }
4258        }
4259        if (command.equals(COMMAND_BARS)) {
4260            String mode = args.getString("mode");
4261            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
4262                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
4263                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
4264                    "transparent".equals(mode) ? MODE_TRANSPARENT :
4265                    "warning".equals(mode) ? MODE_WARNING :
4266                    -1;
4267            if (barMode != -1) {
4268                boolean animate = true;
4269                if (mStatusBarView != null) {
4270                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
4271                }
4272                if (mNavigationBar != null) {
4273                    mNavigationBar.getBarTransitions().transitionTo(barMode, animate);
4274                }
4275            }
4276        }
4277    }
4278
4279    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
4280        if (mStatusBarView == null) return;
4281        View v = mStatusBarView.findViewById(id);
4282        if (v instanceof DemoMode) {
4283            ((DemoMode)v).dispatchDemoCommand(command, args);
4284        }
4285    }
4286
4287    /**
4288     * @return The {@link StatusBarState} the status bar is in.
4289     */
4290    public int getBarState() {
4291        return mState;
4292    }
4293
4294    public boolean isPanelFullyCollapsed() {
4295        return mNotificationPanel.isFullyCollapsed();
4296    }
4297
4298    public void showKeyguard() {
4299        mKeyguardRequested = true;
4300        mLeaveOpenOnKeyguardHide = false;
4301        mPendingRemoteInputView = null;
4302        updateIsKeyguard();
4303        mAssistManager.onLockscreenShown();
4304    }
4305
4306    public boolean hideKeyguard() {
4307        mKeyguardRequested = false;
4308        return updateIsKeyguard();
4309    }
4310
4311    private boolean updateIsKeyguard() {
4312        boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
4313                == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
4314
4315        // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
4316        // there's no surface we can show to the user. Note that the device goes fully interactive
4317        // late in the transition, so we also allow the device to start dozing once the screen has
4318        // turned off fully.
4319        boolean keyguardForDozing = mDozingRequested &&
4320                (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
4321        boolean shouldBeKeyguard = (mKeyguardRequested || keyguardForDozing) && !wakeAndUnlocking;
4322        if (keyguardForDozing) {
4323            updatePanelExpansionForKeyguard();
4324        }
4325        if (shouldBeKeyguard) {
4326            if (isGoingToSleep()
4327                    && mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_TURNING_OFF) {
4328                // Delay showing the keyguard until screen turned off.
4329            } else {
4330                showKeyguardImpl();
4331            }
4332        } else {
4333            return hideKeyguardImpl();
4334        }
4335        return false;
4336    }
4337
4338    public void showKeyguardImpl() {
4339        mIsKeyguard = true;
4340        if (mLaunchTransitionFadingAway) {
4341            mNotificationPanel.animate().cancel();
4342            onLaunchTransitionFadingEnded();
4343        }
4344        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
4345        if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
4346            setBarState(StatusBarState.FULLSCREEN_USER_SWITCHER);
4347        } else {
4348            setBarState(StatusBarState.KEYGUARD);
4349        }
4350        updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
4351        updatePanelExpansionForKeyguard();
4352        if (mDraggedDownRow != null) {
4353            mDraggedDownRow.setUserLocked(false);
4354            mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
4355            mDraggedDownRow = null;
4356        }
4357    }
4358
4359    private void updatePanelExpansionForKeyguard() {
4360        if (mState == StatusBarState.KEYGUARD && mFingerprintUnlockController.getMode()
4361                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
4362            instantExpandNotificationsPanel();
4363        } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
4364            instantCollapseNotificationPanel();
4365        }
4366    }
4367
4368    private void onLaunchTransitionFadingEnded() {
4369        mNotificationPanel.setAlpha(1.0f);
4370        mNotificationPanel.onAffordanceLaunchEnded();
4371        releaseGestureWakeLock();
4372        runLaunchTransitionEndRunnable();
4373        mLaunchTransitionFadingAway = false;
4374        mScrimController.forceHideScrims(false /* hide */, false /* animated */);
4375        updateMediaMetaData(true /* metaDataChanged */, true);
4376    }
4377
4378    public boolean isCollapsing() {
4379        return mNotificationPanel.isCollapsing();
4380    }
4381
4382    public void addPostCollapseAction(Runnable r) {
4383        mPostCollapseRunnables.add(r);
4384    }
4385
4386    public boolean isInLaunchTransition() {
4387        return mNotificationPanel.isLaunchTransitionRunning()
4388                || mNotificationPanel.isLaunchTransitionFinished();
4389    }
4390
4391    /**
4392     * Fades the content of the keyguard away after the launch transition is done.
4393     *
4394     * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
4395     *                     starts
4396     * @param endRunnable the runnable to be run when the transition is done
4397     */
4398    public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
4399            Runnable endRunnable) {
4400        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
4401        mLaunchTransitionEndRunnable = endRunnable;
4402        Runnable hideRunnable = new Runnable() {
4403            @Override
4404            public void run() {
4405                mLaunchTransitionFadingAway = true;
4406                if (beforeFading != null) {
4407                    beforeFading.run();
4408                }
4409                mScrimController.forceHideScrims(true /* hide */, false /* animated */);
4410                updateMediaMetaData(false, true);
4411                mNotificationPanel.setAlpha(1);
4412                mStackScroller.setParentNotFullyVisible(true);
4413                mNotificationPanel.animate()
4414                        .alpha(0)
4415                        .setStartDelay(FADE_KEYGUARD_START_DELAY)
4416                        .setDuration(FADE_KEYGUARD_DURATION)
4417                        .withLayer()
4418                        .withEndAction(new Runnable() {
4419                            @Override
4420                            public void run() {
4421                                onLaunchTransitionFadingEnded();
4422                            }
4423                        });
4424                mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
4425                        LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
4426            }
4427        };
4428        if (mNotificationPanel.isLaunchTransitionRunning()) {
4429            mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
4430        } else {
4431            hideRunnable.run();
4432        }
4433    }
4434
4435    /**
4436     * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
4437     * fading.
4438     */
4439    public void fadeKeyguardWhilePulsing() {
4440        mNotificationPanel.notifyStartFading();
4441        mNotificationPanel.animate()
4442                .alpha(0f)
4443                .setStartDelay(0)
4444                .setDuration(FADE_KEYGUARD_DURATION_PULSING)
4445                .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR)
4446                .start();
4447    }
4448
4449    /**
4450     * Plays the animation when an activity that was occluding Keyguard goes away.
4451     */
4452    public void animateKeyguardUnoccluding() {
4453        mScrimController.animateKeyguardUnoccluding(500);
4454        mNotificationPanel.setExpandedFraction(0f);
4455        animateExpandNotificationsPanel();
4456    }
4457
4458    /**
4459     * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
4460     * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
4461     * because the launched app crashed or something else went wrong.
4462     */
4463    public void startLaunchTransitionTimeout() {
4464        mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT,
4465                LAUNCH_TRANSITION_TIMEOUT_MS);
4466    }
4467
4468    private void onLaunchTransitionTimeout() {
4469        Log.w(TAG, "Launch transition: Timeout!");
4470        mNotificationPanel.onAffordanceLaunchEnded();
4471        releaseGestureWakeLock();
4472        mNotificationPanel.resetViews();
4473    }
4474
4475    private void runLaunchTransitionEndRunnable() {
4476        if (mLaunchTransitionEndRunnable != null) {
4477            Runnable r = mLaunchTransitionEndRunnable;
4478
4479            // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again,
4480            // which would lead to infinite recursion. Protect against it.
4481            mLaunchTransitionEndRunnable = null;
4482            r.run();
4483        }
4484    }
4485
4486    /**
4487     * @return true if we would like to stay in the shade, false if it should go away entirely
4488     */
4489    public boolean hideKeyguardImpl() {
4490        mIsKeyguard = false;
4491        Trace.beginSection("StatusBar#hideKeyguard");
4492        boolean staying = mLeaveOpenOnKeyguardHide;
4493        setBarState(StatusBarState.SHADE);
4494        View viewToClick = null;
4495        if (mLeaveOpenOnKeyguardHide) {
4496            if (!mKeyguardRequested) {
4497                mLeaveOpenOnKeyguardHide = false;
4498            }
4499            long delay = calculateGoingToFullShadeDelay();
4500            mNotificationPanel.animateToFullShade(delay);
4501            if (mDraggedDownRow != null) {
4502                mDraggedDownRow.setUserLocked(false);
4503                mDraggedDownRow = null;
4504            }
4505            if (!mKeyguardRequested) {
4506                viewToClick = mPendingRemoteInputView;
4507                mPendingRemoteInputView = null;
4508            }
4509
4510            // Disable layout transitions in navbar for this transition because the load is just
4511            // too heavy for the CPU and GPU on any device.
4512            if (mNavigationBar != null) {
4513                mNavigationBar.disableAnimationsDuringHide(delay);
4514            }
4515        } else if (!mNotificationPanel.isCollapsing()) {
4516            instantCollapseNotificationPanel();
4517        }
4518        updateKeyguardState(staying, false /* fromShadeLocked */);
4519
4520        if (viewToClick != null && viewToClick.isAttachedToWindow()) {
4521            viewToClick.callOnClick();
4522        }
4523
4524        // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
4525        // visibilities so next time we open the panel we know the correct height already.
4526        if (mQSPanel != null) {
4527            mQSPanel.refreshAllTiles();
4528        }
4529        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
4530        releaseGestureWakeLock();
4531        mNotificationPanel.onAffordanceLaunchEnded();
4532        mNotificationPanel.animate().cancel();
4533        mNotificationPanel.setAlpha(1f);
4534        Trace.endSection();
4535        return staying;
4536    }
4537
4538    private void releaseGestureWakeLock() {
4539        if (mGestureWakeLock.isHeld()) {
4540            mGestureWakeLock.release();
4541        }
4542    }
4543
4544    public long calculateGoingToFullShadeDelay() {
4545        return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
4546    }
4547
4548    /**
4549     * Notifies the status bar that Keyguard is going away very soon.
4550     */
4551    public void keyguardGoingAway() {
4552
4553        // Treat Keyguard exit animation as an app transition to achieve nice transition for status
4554        // bar.
4555        mKeyguardGoingAway = true;
4556        mKeyguardMonitor.notifyKeyguardGoingAway(true);
4557        mCommandQueue.appTransitionPending(true);
4558    }
4559
4560    /**
4561     * Notifies the status bar the Keyguard is fading away with the specified timings.
4562     *
4563     * @param startTime the start time of the animations in uptime millis
4564     * @param delay the precalculated animation delay in miliseconds
4565     * @param fadeoutDuration the duration of the exit animation, in milliseconds
4566     */
4567    public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
4568        mKeyguardFadingAway = true;
4569        mKeyguardFadingAwayDelay = delay;
4570        mKeyguardFadingAwayDuration = fadeoutDuration;
4571        mWaitingForKeyguardExit = false;
4572        mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
4573                        - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
4574                LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
4575        recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
4576        mCommandQueue.appTransitionStarting(
4577                    startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
4578                    LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
4579        mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration);
4580    }
4581
4582    public boolean isKeyguardFadingAway() {
4583        return mKeyguardFadingAway;
4584    }
4585
4586    /**
4587     * Notifies that the Keyguard fading away animation is done.
4588     */
4589    public void finishKeyguardFadingAway() {
4590        mKeyguardFadingAway = false;
4591        mKeyguardGoingAway = false;
4592        mKeyguardMonitor.notifyKeyguardDoneFading();
4593    }
4594
4595    public void stopWaitingForKeyguardExit() {
4596        mWaitingForKeyguardExit = false;
4597    }
4598
4599    private void updatePublicMode() {
4600        final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
4601        final boolean devicePublic = showingKeyguard
4602                && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId);
4603
4604        // Look for public mode users. Users are considered public in either case of:
4605        //   - device keyguard is shown in secure mode;
4606        //   - profile is locked with a work challenge.
4607        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
4608            final int userId = mCurrentProfiles.valueAt(i).id;
4609            boolean isProfilePublic = devicePublic;
4610            if (!devicePublic && userId != mCurrentUserId) {
4611                // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
4612                // due to a race condition where this code could be called before
4613                // TrustManagerService updates its internal records, resulting in an incorrect
4614                // state being cached in mLockscreenPublicMode. (b/35951989)
4615                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
4616                        && mStatusBarKeyguardViewManager.isSecure(userId)) {
4617                    isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
4618                }
4619            }
4620            setLockscreenPublicMode(isProfilePublic, userId);
4621        }
4622    }
4623
4624    protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
4625        Trace.beginSection("StatusBar#updateKeyguardState");
4626        if (mState == StatusBarState.KEYGUARD) {
4627            mKeyguardIndicationController.setVisible(true);
4628            mNotificationPanel.resetViews();
4629            if (mKeyguardUserSwitcher != null) {
4630                mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
4631            }
4632            if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables();
4633            if (mAmbientIndicationContainer != null) {
4634                mAmbientIndicationContainer.setVisibility(View.VISIBLE);
4635            }
4636        } else {
4637            mKeyguardIndicationController.setVisible(false);
4638            if (mKeyguardUserSwitcher != null) {
4639                mKeyguardUserSwitcher.setKeyguard(false,
4640                        goingToFullShade ||
4641                        mState == StatusBarState.SHADE_LOCKED ||
4642                        fromShadeLocked);
4643            }
4644            if (mAmbientIndicationContainer != null) {
4645                mAmbientIndicationContainer.setVisibility(View.INVISIBLE);
4646            }
4647        }
4648        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
4649            mScrimController.setKeyguardShowing(true);
4650        } else {
4651            mScrimController.setKeyguardShowing(false);
4652        }
4653        mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
4654        updateTheme();
4655        updateDozingState();
4656        updatePublicMode();
4657        updateStackScrollerState(goingToFullShade, fromShadeLocked);
4658        updateNotifications();
4659        checkBarModes();
4660        updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
4661        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
4662                mUnlockMethodCache.isMethodSecure(),
4663                mStatusBarKeyguardViewManager.isOccluded());
4664        Trace.endSection();
4665    }
4666
4667    /**
4668     * Switches theme from light to dark and vice-versa.
4669     */
4670    protected void updateTheme() {
4671        final boolean inflated = mStackScroller != null;
4672
4673        // The system wallpaper defines if QS should be light or dark.
4674        WallpaperColors systemColors = mColorExtractor
4675                .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
4676        final boolean useDarkTheme = systemColors != null
4677                && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
4678        if (isUsingDarkTheme() != useDarkTheme) {
4679            try {
4680                mOverlayManager.setEnabled("com.android.systemui.theme.dark",
4681                        useDarkTheme, mCurrentUserId);
4682            } catch (RemoteException e) {
4683                Log.w(TAG, "Can't change theme", e);
4684            }
4685        }
4686
4687        // Lock wallpaper defines the color of the majority of the views, hence we'll use it
4688        // to set our default theme.
4689        final boolean lockDarkText = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, true
4690                /* ignoreVisibility */).supportsDarkText();
4691        final int themeResId = lockDarkText ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI;
4692        if (mContext.getThemeResId() != themeResId) {
4693            mContext.setTheme(themeResId);
4694            if (inflated) {
4695                reinflateViews();
4696            }
4697        }
4698
4699        if (inflated) {
4700            int which;
4701            if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
4702                which = WallpaperManager.FLAG_LOCK;
4703            } else {
4704                which = WallpaperManager.FLAG_SYSTEM;
4705            }
4706            final boolean useDarkText = mColorExtractor.getColors(which,
4707                    true /* ignoreVisibility */).supportsDarkText();
4708            mStackScroller.updateDecorViews(useDarkText);
4709
4710            // Make sure we have the correct navbar/statusbar colors.
4711            mStatusBarWindowManager.setKeyguardDark(useDarkText);
4712        }
4713    }
4714
4715    private void updateDozingState() {
4716        Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
4717        Trace.beginSection("StatusBar#updateDozingState");
4718        boolean animate = !mDozing && mDozeServiceHost.shouldAnimateWakeup();
4719        mNotificationPanel.setDozing(mDozing, animate);
4720        mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
4721        mScrimController.setDozing(mDozing);
4722        mKeyguardIndicationController.setDozing(mDozing);
4723        mNotificationPanel.setDark(mDozing, animate);
4724        updateQsExpansionEnabled();
4725        mDozeScrimController.setDozing(mDozing, animate);
4726        updateRowStates();
4727        Trace.endSection();
4728    }
4729
4730    public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
4731        if (mStackScroller == null) return;
4732        boolean onKeyguard = mState == StatusBarState.KEYGUARD;
4733        boolean publicMode = isAnyProfilePublicMode();
4734        mStackScroller.setHideSensitive(publicMode, goingToFullShade);
4735        mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
4736        mStackScroller.setExpandingEnabled(!onKeyguard);
4737        ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
4738        mStackScroller.setActivatedChild(null);
4739        if (activatedChild != null) {
4740            activatedChild.makeInactive(false /* animate */);
4741        }
4742    }
4743
4744    public void userActivity() {
4745        if (mState == StatusBarState.KEYGUARD) {
4746            mKeyguardViewMediatorCallback.userActivity();
4747        }
4748    }
4749
4750    public boolean interceptMediaKey(KeyEvent event) {
4751        return mState == StatusBarState.KEYGUARD
4752                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
4753    }
4754
4755    protected boolean shouldUnlockOnMenuPressed() {
4756        return mDeviceInteractive && mState != StatusBarState.SHADE
4757            && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed();
4758    }
4759
4760    public boolean onMenuPressed() {
4761        if (shouldUnlockOnMenuPressed()) {
4762            animateCollapsePanels(
4763                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
4764            return true;
4765        }
4766        return false;
4767    }
4768
4769    public void endAffordanceLaunch() {
4770        releaseGestureWakeLock();
4771        mNotificationPanel.onAffordanceLaunchEnded();
4772    }
4773
4774    public boolean onBackPressed() {
4775        if (mStatusBarKeyguardViewManager.onBackPressed()) {
4776            return true;
4777        }
4778        if (mNotificationPanel.isQsExpanded()) {
4779            if (mNotificationPanel.isQsDetailShowing()) {
4780                mNotificationPanel.closeQsDetail();
4781            } else {
4782                mNotificationPanel.animateCloseQs();
4783            }
4784            return true;
4785        }
4786        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
4787            animateCollapsePanels();
4788            return true;
4789        }
4790        if (mKeyguardUserSwitcher != null && mKeyguardUserSwitcher.hideIfNotSimple(true)) {
4791            return true;
4792        }
4793        return false;
4794    }
4795
4796    public boolean onSpacePressed() {
4797        if (mDeviceInteractive && mState != StatusBarState.SHADE) {
4798            animateCollapsePanels(
4799                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
4800            return true;
4801        }
4802        return false;
4803    }
4804
4805    private void showBouncerIfKeyguard() {
4806        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
4807            showBouncer();
4808        }
4809    }
4810
4811    protected void showBouncer() {
4812        mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
4813        mStatusBarKeyguardViewManager.dismiss();
4814    }
4815
4816    private void instantExpandNotificationsPanel() {
4817        // Make our window larger and the panel expanded.
4818        makeExpandedVisible(true);
4819        mNotificationPanel.expand(false /* animate */);
4820        recomputeDisableFlags(false /* animate */);
4821    }
4822
4823    private void instantCollapseNotificationPanel() {
4824        mNotificationPanel.instantCollapse();
4825    }
4826
4827    @Override
4828    public void onActivated(ActivatableNotificationView view) {
4829        onActivated((View)view);
4830        mStackScroller.setActivatedChild(view);
4831    }
4832
4833    public void onActivated(View view) {
4834        mLockscreenGestureLogger.write(
4835                MetricsEvent.ACTION_LS_NOTE,
4836                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
4837        mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
4838        ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
4839        if (previousView != null) {
4840            previousView.makeInactive(true /* animate */);
4841        }
4842    }
4843
4844    /**
4845     * @param state The {@link StatusBarState} to set.
4846     */
4847    public void setBarState(int state) {
4848        // If we're visible and switched to SHADE_LOCKED (the user dragged
4849        // down on the lockscreen), clear notification LED, vibration,
4850        // ringing.
4851        // Other transitions are covered in handleVisibleToUserChanged().
4852        if (state != mState && mVisible && (state == StatusBarState.SHADE_LOCKED
4853                || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) {
4854            clearNotificationEffects();
4855        }
4856        if (state == StatusBarState.KEYGUARD) {
4857            removeRemoteInputEntriesKeptUntilCollapsed();
4858            maybeEscalateHeadsUp();
4859        }
4860        mState = state;
4861        mGroupManager.setStatusBarState(state);
4862        mHeadsUpManager.setStatusBarState(state);
4863        mFalsingManager.setStatusBarState(state);
4864        mStatusBarWindowManager.setStatusBarState(state);
4865        mStackScroller.setStatusBarState(state);
4866        updateReportRejectedTouchVisibility();
4867        updateDozing();
4868        updateTheme();
4869        touchAutoDim();
4870        mNotificationShelf.setStatusBarState(state);
4871    }
4872
4873    @Override
4874    public void onActivationReset(ActivatableNotificationView view) {
4875        if (view == mStackScroller.getActivatedChild()) {
4876            mStackScroller.setActivatedChild(null);
4877            onActivationReset((View)view);
4878        }
4879    }
4880
4881    public void onActivationReset(View view) {
4882        mKeyguardIndicationController.hideTransientIndication();
4883    }
4884
4885    public void onTrackingStarted() {
4886        runPostCollapseRunnables();
4887    }
4888
4889    public void onClosingFinished() {
4890        runPostCollapseRunnables();
4891        if (!isPanelFullyCollapsed()) {
4892            // if we set it not to be focusable when collapsing, we have to undo it when we aborted
4893            // the closing
4894            mStatusBarWindowManager.setStatusBarFocusable(true);
4895        }
4896    }
4897
4898    public void onUnlockHintStarted() {
4899        mFalsingManager.onUnlockHintStarted();
4900        mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
4901    }
4902
4903    public void onHintFinished() {
4904        // Delay the reset a bit so the user can read the text.
4905        mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
4906    }
4907
4908    public void onCameraHintStarted() {
4909        mFalsingManager.onCameraHintStarted();
4910        mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
4911    }
4912
4913    public void onVoiceAssistHintStarted() {
4914        mFalsingManager.onLeftAffordanceHintStarted();
4915        mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
4916    }
4917
4918    public void onPhoneHintStarted() {
4919        mFalsingManager.onLeftAffordanceHintStarted();
4920        mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
4921    }
4922
4923    public void onTrackingStopped(boolean expand) {
4924        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
4925            if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
4926                showBouncerIfKeyguard();
4927            }
4928        }
4929    }
4930
4931    protected int getMaxKeyguardNotifications(boolean recompute) {
4932        if (recompute) {
4933            mMaxKeyguardNotifications = Math.max(1,
4934                    mNotificationPanel.computeMaxKeyguardNotifications(
4935                            mMaxAllowedKeyguardNotifications));
4936            return mMaxKeyguardNotifications;
4937        }
4938        return mMaxKeyguardNotifications;
4939    }
4940
4941    public int getMaxKeyguardNotifications() {
4942        return getMaxKeyguardNotifications(false /* recompute */);
4943    }
4944
4945    // TODO: Figure out way to remove these.
4946    public NavigationBarView getNavigationBarView() {
4947        return (mNavigationBar != null ? (NavigationBarView) mNavigationBar.getView() : null);
4948    }
4949
4950    public View getNavigationBarWindow() {
4951        return mNavigationBarView;
4952    }
4953
4954    /**
4955     * TODO: Remove this method. Views should not be passed forward. Will cause theme issues.
4956     * @return bottom area view
4957     */
4958    public KeyguardBottomAreaView getKeyguardBottomAreaView() {
4959        return mNotificationPanel.getKeyguardBottomAreaView();
4960    }
4961
4962    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
4963
4964
4965    /* Only ever called as a consequence of a lockscreen expansion gesture. */
4966    @Override
4967    public boolean onDraggedDown(View startingChild, int dragLengthY) {
4968        if (mState == StatusBarState.KEYGUARD
4969                && hasActiveNotifications() && (!isDozing() || isPulsing())) {
4970            mLockscreenGestureLogger.write(
4971                    MetricsEvent.ACTION_LS_SHADE,
4972                    (int) (dragLengthY / mDisplayMetrics.density),
4973                    0 /* velocityDp - N/A */);
4974
4975            // We have notifications, go to locked shade.
4976            goToLockedShade(startingChild);
4977            if (startingChild instanceof ExpandableNotificationRow) {
4978                ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
4979                row.onExpandedByGesture(true /* drag down is always an open */);
4980            }
4981            return true;
4982        } else {
4983            // abort gesture.
4984            return false;
4985        }
4986    }
4987
4988    @Override
4989    public void onDragDownReset() {
4990        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
4991        mStackScroller.resetScrollPosition();
4992        mStackScroller.resetCheckSnoozeLeavebehind();
4993    }
4994
4995    @Override
4996    public void onCrossedThreshold(boolean above) {
4997        mStackScroller.setDimmed(!above /* dimmed */, true /* animate */);
4998    }
4999
5000    @Override
5001    public void onTouchSlopExceeded() {
5002        mStackScroller.removeLongPressCallback();
5003        mStackScroller.checkSnoozeLeavebehind();
5004    }
5005
5006    @Override
5007    public void setEmptyDragAmount(float amount) {
5008        mNotificationPanel.setEmptyDragAmount(amount);
5009    }
5010
5011    @Override
5012    public boolean isFalsingCheckNeeded() {
5013        return mState == StatusBarState.KEYGUARD;
5014    }
5015
5016    /**
5017     * If secure with redaction: Show bouncer, go to unlocked shade.
5018     *
5019     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
5020     *
5021     * @param expandView The view to expand after going to the shade.
5022     */
5023    public void goToLockedShade(View expandView) {
5024        int userId = mCurrentUserId;
5025        ExpandableNotificationRow row = null;
5026        if (expandView instanceof ExpandableNotificationRow) {
5027            row = (ExpandableNotificationRow) expandView;
5028            row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
5029            // Indicate that the group expansion is changing at this time -- this way the group
5030            // and children backgrounds / divider animations will look correct.
5031            row.setGroupExpansionChanging(true);
5032            if (row.getStatusBarNotification() != null) {
5033                userId = row.getStatusBarNotification().getUserId();
5034            }
5035        }
5036        boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
5037                || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
5038        if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
5039            mLeaveOpenOnKeyguardHide = true;
5040            showBouncerIfKeyguard();
5041            mDraggedDownRow = row;
5042            mPendingRemoteInputView = null;
5043        } else {
5044            mNotificationPanel.animateToFullShade(0 /* delay */);
5045            setBarState(StatusBarState.SHADE_LOCKED);
5046            updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
5047        }
5048    }
5049
5050    public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
5051        mLeaveOpenOnKeyguardHide = true;
5052        dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
5053    }
5054
5055    protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
5056        mLeaveOpenOnKeyguardHide = true;
5057        showBouncer();
5058        mPendingRemoteInputView = clicked;
5059    }
5060
5061    protected void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
5062            View clickedView) {
5063        if (isKeyguardShowing()) {
5064            onLockedRemoteInput(row, clickedView);
5065        } else {
5066            row.setUserExpanded(true);
5067            row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
5068        }
5069    }
5070
5071    protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
5072            String notificationKey) {
5073        // Clear pending remote view, as we do not want to trigger pending remote input view when
5074        // it's called by other code
5075        mPendingWorkRemoteInputView = null;
5076        // Begin old BaseStatusBar.startWorkChallengeIfNecessary.
5077        final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
5078                null, userId);
5079        if (newIntent == null) {
5080            return false;
5081        }
5082        final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
5083        callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
5084        callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
5085        callBackIntent.setPackage(mContext.getPackageName());
5086
5087        PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
5088                mContext,
5089                0,
5090                callBackIntent,
5091                PendingIntent.FLAG_CANCEL_CURRENT |
5092                        PendingIntent.FLAG_ONE_SHOT |
5093                        PendingIntent.FLAG_IMMUTABLE);
5094        newIntent.putExtra(
5095                Intent.EXTRA_INTENT,
5096                callBackPendingIntent.getIntentSender());
5097        try {
5098            ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
5099                    null /*options*/);
5100        } catch (RemoteException ex) {
5101            // ignore
5102        }
5103        return true;
5104        // End old BaseStatusBar.startWorkChallengeIfNecessary.
5105    }
5106
5107    protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
5108            View clicked) {
5109        // Collapse notification and show work challenge
5110        animateCollapsePanels();
5111        startWorkChallengeIfNecessary(userId, null, null);
5112        // Add pending remote input view after starting work challenge, as starting work challenge
5113        // will clear all previous pending review view
5114        mPendingWorkRemoteInputView = clicked;
5115    }
5116
5117    private boolean isAnyProfilePublicMode() {
5118        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
5119            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
5120                return true;
5121            }
5122        }
5123        return false;
5124    }
5125
5126    protected void onWorkChallengeChanged() {
5127        updatePublicMode();
5128        updateNotifications();
5129        if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
5130            // Expand notification panel and the notification row, then click on remote input view
5131            final Runnable clickPendingViewRunnable = new Runnable() {
5132                @Override
5133                public void run() {
5134                    final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
5135                    if (pendingWorkRemoteInputView == null) {
5136                        return;
5137                    }
5138
5139                    // Climb up the hierarchy until we get to the container for this row.
5140                    ViewParent p = pendingWorkRemoteInputView.getParent();
5141                    while (!(p instanceof ExpandableNotificationRow)) {
5142                        if (p == null) {
5143                            return;
5144                        }
5145                        p = p.getParent();
5146                    }
5147
5148                    final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
5149                    ViewParent viewParent = row.getParent();
5150                    if (viewParent instanceof NotificationStackScrollLayout) {
5151                        final NotificationStackScrollLayout scrollLayout =
5152                                (NotificationStackScrollLayout) viewParent;
5153                        row.makeActionsVisibile();
5154                        row.post(new Runnable() {
5155                            @Override
5156                            public void run() {
5157                                final Runnable finishScrollingCallback = new Runnable() {
5158                                    @Override
5159                                    public void run() {
5160                                        mPendingWorkRemoteInputView.callOnClick();
5161                                        mPendingWorkRemoteInputView = null;
5162                                        scrollLayout.setFinishScrollingCallback(null);
5163                                    }
5164                                };
5165                                if (scrollLayout.scrollTo(row)) {
5166                                    // It scrolls! So call it when it's finished.
5167                                    scrollLayout.setFinishScrollingCallback(
5168                                            finishScrollingCallback);
5169                                } else {
5170                                    // It does not scroll, so call it now!
5171                                    finishScrollingCallback.run();
5172                                }
5173                            }
5174                        });
5175                    }
5176                }
5177            };
5178            mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
5179                    new ViewTreeObserver.OnGlobalLayoutListener() {
5180                        @Override
5181                        public void onGlobalLayout() {
5182                            if (mNotificationPanel.mStatusBar.getStatusBarWindow()
5183                                    .getHeight() != mNotificationPanel.mStatusBar
5184                                            .getStatusBarHeight()) {
5185                                mNotificationPanel.getViewTreeObserver()
5186                                        .removeOnGlobalLayoutListener(this);
5187                                mNotificationPanel.post(clickPendingViewRunnable);
5188                            }
5189                        }
5190                    });
5191            instantExpandNotificationsPanel();
5192        }
5193    }
5194
5195    @Override
5196    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
5197        mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
5198        if (mState == StatusBarState.KEYGUARD && nowExpanded) {
5199            goToLockedShade(clickedEntry.row);
5200        }
5201    }
5202
5203    /**
5204     * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
5205     */
5206    public void goToKeyguard() {
5207        if (mState == StatusBarState.SHADE_LOCKED) {
5208            mStackScroller.onGoToKeyguard();
5209            setBarState(StatusBarState.KEYGUARD);
5210            updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
5211        }
5212    }
5213
5214    public long getKeyguardFadingAwayDelay() {
5215        return mKeyguardFadingAwayDelay;
5216    }
5217
5218    public long getKeyguardFadingAwayDuration() {
5219        return mKeyguardFadingAwayDuration;
5220    }
5221
5222    public void setBouncerShowing(boolean bouncerShowing) {
5223        mBouncerShowing = bouncerShowing;
5224        if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
5225        updateHideIconsForBouncer(true /* animate */);
5226        recomputeDisableFlags(true /* animate */);
5227    }
5228
5229    public void cancelCurrentTouch() {
5230        if (mNotificationPanel.isTracking()) {
5231            mStatusBarWindow.cancelCurrentTouch();
5232            if (mState == StatusBarState.SHADE) {
5233                animateCollapsePanels();
5234            }
5235        }
5236    }
5237
5238    WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
5239        @Override
5240        public void onFinishedGoingToSleep() {
5241            mNotificationPanel.onAffordanceLaunchEnded();
5242            releaseGestureWakeLock();
5243            mLaunchCameraOnScreenTurningOn = false;
5244            mDeviceInteractive = false;
5245            mWakeUpComingFromTouch = false;
5246            mWakeUpTouchLocation = null;
5247            mStackScroller.setAnimationsEnabled(false);
5248            mVisualStabilityManager.setScreenOn(false);
5249            updateVisibleToUser();
5250
5251            // We need to disable touch events because these might
5252            // collapse the panel after we expanded it, and thus we would end up with a blank
5253            // Keyguard.
5254            mNotificationPanel.setTouchDisabled(true);
5255            mStatusBarWindow.cancelCurrentTouch();
5256            if (mLaunchCameraOnFinishedGoingToSleep) {
5257                mLaunchCameraOnFinishedGoingToSleep = false;
5258
5259                // This gets executed before we will show Keyguard, so post it in order that the state
5260                // is correct.
5261                mHandler.post(new Runnable() {
5262                    @Override
5263                    public void run() {
5264                        onCameraLaunchGestureDetected(mLastCameraLaunchSource);
5265                    }
5266                });
5267            }
5268            updateIsKeyguard();
5269        }
5270
5271        @Override
5272        public void onStartedGoingToSleep() {
5273            notifyHeadsUpGoingToSleep();
5274            dismissVolumeDialog();
5275        }
5276
5277        @Override
5278        public void onStartedWakingUp() {
5279            mDeviceInteractive = true;
5280            mStackScroller.setAnimationsEnabled(true);
5281            mVisualStabilityManager.setScreenOn(true);
5282            mNotificationPanel.setTouchDisabled(false);
5283
5284            maybePrepareWakeUpFromAod();
5285
5286            mDozeServiceHost.stopDozing();
5287            updateVisibleToUser();
5288            updateIsKeyguard();
5289        }
5290    };
5291
5292    ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
5293        @Override
5294        public void onScreenTurningOn() {
5295            mFalsingManager.onScreenTurningOn();
5296            mNotificationPanel.onScreenTurningOn();
5297
5298            maybePrepareWakeUpFromAod();
5299
5300            if (mLaunchCameraOnScreenTurningOn) {
5301                mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
5302                mLaunchCameraOnScreenTurningOn = false;
5303            }
5304        }
5305
5306        @Override
5307        public void onScreenTurnedOn() {
5308            mScrimController.wakeUpFromAod();
5309            mDozeScrimController.onScreenTurnedOn();
5310        }
5311
5312        @Override
5313        public void onScreenTurnedOff() {
5314            mFalsingManager.onScreenOff();
5315            // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
5316            // in that case destroys the HeadsUpManager state, so don't do it in that case.
5317            if (!isPulsing()) {
5318                updateIsKeyguard();
5319            }
5320        }
5321    };
5322
5323    public int getWakefulnessState() {
5324        return mWakefulnessLifecycle.getWakefulness();
5325    }
5326
5327    private void maybePrepareWakeUpFromAod() {
5328        int wakefulness = mWakefulnessLifecycle.getWakefulness();
5329        if (mDozing && wakefulness == WAKEFULNESS_WAKING && !isPulsing()) {
5330            mScrimController.prepareWakeUpFromAod();
5331        }
5332    }
5333
5334    private void vibrateForCameraGesture() {
5335        // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
5336        mVibrator.vibrate(mCameraLaunchGestureVibePattern, -1 /* repeat */);
5337    }
5338
5339    /**
5340     * @return true if the screen is currently fully off, i.e. has finished turning off and has
5341     *         since not started turning on.
5342     */
5343    public boolean isScreenFullyOff() {
5344        return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
5345    }
5346
5347    @Override
5348    public void showScreenPinningRequest(int taskId) {
5349        if (mKeyguardMonitor.isShowing()) {
5350            // Don't allow apps to trigger this from keyguard.
5351            return;
5352        }
5353        // Show screen pinning request, since this comes from an app, show 'no thanks', button.
5354        showScreenPinningRequest(taskId, true);
5355    }
5356
5357    public void showScreenPinningRequest(int taskId, boolean allowCancel) {
5358        mScreenPinningRequest.showPrompt(taskId, allowCancel);
5359    }
5360
5361    public boolean hasActiveNotifications() {
5362        return !mNotificationData.getActiveNotifications().isEmpty();
5363    }
5364
5365    public void wakeUpIfDozing(long time, View where) {
5366        if (mDozing) {
5367            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
5368            pm.wakeUp(time, "com.android.systemui:NODOZE");
5369            mWakeUpComingFromTouch = true;
5370            where.getLocationInWindow(mTmpInt2);
5371            mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
5372                    mTmpInt2[1] + where.getHeight() / 2);
5373            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
5374            mFalsingManager.onScreenOnFromTouch();
5375        }
5376    }
5377
5378    @Override
5379    public void appTransitionCancelled() {
5380        EventBus.getDefault().send(new AppTransitionFinishedEvent());
5381    }
5382
5383    @Override
5384    public void appTransitionFinished() {
5385        EventBus.getDefault().send(new AppTransitionFinishedEvent());
5386    }
5387
5388    @Override
5389    public void onCameraLaunchGestureDetected(int source) {
5390        mLastCameraLaunchSource = source;
5391        if (isGoingToSleep()) {
5392            if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Finish going to sleep before launching camera");
5393            mLaunchCameraOnFinishedGoingToSleep = true;
5394            return;
5395        }
5396        if (!mNotificationPanel.canCameraGestureBeLaunched(
5397                mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
5398            if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now, mExpandedVisible: " +
5399                    mExpandedVisible);
5400            return;
5401        }
5402        if (!mDeviceInteractive) {
5403            PowerManager pm = mContext.getSystemService(PowerManager.class);
5404            pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
5405            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
5406        }
5407        vibrateForCameraGesture();
5408        if (!mStatusBarKeyguardViewManager.isShowing()) {
5409            startActivityDismissingKeyguard(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
5410                    false /* onlyProvisioned */, true /* dismissShade */,
5411                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */);
5412        } else {
5413            if (!mDeviceInteractive) {
5414                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
5415                // comes on.
5416                mScrimController.dontAnimateBouncerChangesUntilNextFrame();
5417                mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
5418            }
5419            if (isScreenTurningOnOrOn()) {
5420                if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
5421                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
5422            } else {
5423                // We need to defer the camera launch until the screen comes on, since otherwise
5424                // we will dismiss us too early since we are waiting on an activity to be drawn and
5425                // incorrectly get notified because of the screen on event (which resumes and pauses
5426                // some activities)
5427                if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on");
5428                mLaunchCameraOnScreenTurningOn = true;
5429            }
5430        }
5431    }
5432
5433    boolean isCameraAllowedByAdmin() {
5434        if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) {
5435            return false;
5436        } else if (isKeyguardShowing() && isKeyguardSecure()) {
5437            // Check if the admin has disabled the camera specifically for the keyguard
5438            return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId)
5439                    & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
5440        }
5441
5442        return true;
5443    }
5444
5445    private boolean isGoingToSleep() {
5446        return mWakefulnessLifecycle.getWakefulness()
5447                == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
5448    }
5449
5450    private boolean isScreenTurningOnOrOn() {
5451        return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_TURNING_ON
5452                || mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
5453    }
5454
5455    public void notifyFpAuthModeChanged() {
5456        updateDozing();
5457    }
5458
5459    private void updateDozing() {
5460        Trace.beginSection("StatusBar#updateDozing");
5461        // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
5462        mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
5463                || mFingerprintUnlockController.getMode()
5464                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
5465        // When in wake-and-unlock we may not have received a change to mState
5466        // but we still should not be dozing, manually set to false.
5467        if (mFingerprintUnlockController.getMode() ==
5468                FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
5469            mDozing = false;
5470        }
5471        mStatusBarWindowManager.setDozing(mDozing);
5472        mStatusBarKeyguardViewManager.setDozing(mDozing);
5473        if (mAmbientIndicationContainer instanceof DozeReceiver) {
5474            ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
5475        }
5476        updateDozingState();
5477        Trace.endSection();
5478    }
5479
5480    public boolean isKeyguardShowing() {
5481        if (mStatusBarKeyguardViewManager == null) {
5482            Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
5483            return true;
5484        }
5485        return mStatusBarKeyguardViewManager.isShowing();
5486    }
5487
5488    private final class DozeServiceHost implements DozeHost {
5489        private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
5490        private boolean mAnimateWakeup;
5491        private boolean mIgnoreTouchWhilePulsing;
5492
5493        @Override
5494        public String toString() {
5495            return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
5496        }
5497
5498        public void firePowerSaveChanged(boolean active) {
5499            for (Callback callback : mCallbacks) {
5500                callback.onPowerSaveChanged(active);
5501            }
5502        }
5503
5504        public void fireNotificationHeadsUp() {
5505            for (Callback callback : mCallbacks) {
5506                callback.onNotificationHeadsUp();
5507            }
5508        }
5509
5510        @Override
5511        public void addCallback(@NonNull Callback callback) {
5512            mCallbacks.add(callback);
5513        }
5514
5515        @Override
5516        public void removeCallback(@NonNull Callback callback) {
5517            mCallbacks.remove(callback);
5518        }
5519
5520        @Override
5521        public void startDozing() {
5522            if (!mDozingRequested) {
5523                mDozingRequested = true;
5524                DozeLog.traceDozing(mContext, mDozing);
5525                updateDozing();
5526                updateIsKeyguard();
5527            }
5528        }
5529
5530        @Override
5531        public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
5532            if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
5533                mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
5534                startAssist(new Bundle());
5535                return;
5536            }
5537
5538            mDozeScrimController.pulse(new PulseCallback() {
5539
5540                @Override
5541                public void onPulseStarted() {
5542                    callback.onPulseStarted();
5543                    Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
5544                            mHeadsUpManager.getAllEntries();
5545                    if (!pulsingEntries.isEmpty()) {
5546                        // Only pulse the stack scroller if there's actually something to show.
5547                        // Otherwise just show the always-on screen.
5548                        setPulsing(pulsingEntries);
5549                    }
5550                }
5551
5552                @Override
5553                public void onPulseFinished() {
5554                    callback.onPulseFinished();
5555                    setPulsing(null);
5556                }
5557
5558                private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
5559                    mStackScroller.setPulsing(pulsing);
5560                    mNotificationPanel.setPulsing(pulsing != null);
5561                    mVisualStabilityManager.setPulsing(pulsing != null);
5562                    mIgnoreTouchWhilePulsing = false;
5563                }
5564            }, reason);
5565        }
5566
5567        @Override
5568        public void stopDozing() {
5569            if (mDozingRequested) {
5570                mDozingRequested = false;
5571                DozeLog.traceDozing(mContext, mDozing);
5572                updateDozing();
5573            }
5574        }
5575
5576        @Override
5577        public void onIgnoreTouchWhilePulsing(boolean ignore) {
5578            if (ignore != mIgnoreTouchWhilePulsing) {
5579                DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
5580            }
5581            mIgnoreTouchWhilePulsing = ignore;
5582            if (isDozing() && ignore) {
5583                mStatusBarWindow.cancelCurrentTouch();
5584            }
5585        }
5586
5587        @Override
5588        public void dozeTimeTick() {
5589            mNotificationPanel.refreshTime();
5590        }
5591
5592        @Override
5593        public boolean isPowerSaveActive() {
5594            return mBatteryController.isPowerSave();
5595        }
5596
5597        @Override
5598        public boolean isPulsingBlocked() {
5599            return mFingerprintUnlockController.getMode()
5600                    == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
5601        }
5602
5603        @Override
5604        public boolean isProvisioned() {
5605            return mDeviceProvisionedController.isDeviceProvisioned()
5606                    && mDeviceProvisionedController.isCurrentUserSetup();
5607        }
5608
5609        @Override
5610        public boolean isBlockingDoze() {
5611            if (mFingerprintUnlockController.hasPendingAuthentication()) {
5612                Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
5613                return true;
5614            }
5615            return false;
5616        }
5617
5618        @Override
5619        public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
5620            StatusBar.this.startPendingIntentDismissingKeyguard(intent);
5621        }
5622
5623        @Override
5624        public void abortPulsing() {
5625            mDozeScrimController.abortPulsing();
5626        }
5627
5628        @Override
5629        public void extendPulse() {
5630            mDozeScrimController.extendPulse();
5631        }
5632
5633        @Override
5634        public void setAnimateWakeup(boolean animateWakeup) {
5635            if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
5636                    || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) {
5637                // Too late to change the wakeup animation.
5638                return;
5639            }
5640            mAnimateWakeup = animateWakeup;
5641        }
5642
5643        @Override
5644        public void onDoubleTap(float screenX, float screenY) {
5645            if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
5646                && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
5647                mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
5648                float viewX = screenX - mTmpInt2[0];
5649                float viewY = screenY - mTmpInt2[1];
5650                if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
5651                        && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
5652                    dispatchDoubleTap(viewX, viewY);
5653                }
5654            }
5655        }
5656
5657        @Override
5658        public void setDozeScreenBrightness(int value) {
5659            mStatusBarWindowManager.setDozeScreenBrightness(value);
5660        }
5661
5662        @Override
5663        public void setAodDimmingScrim(float scrimOpacity) {
5664            mDozeScrimController.setAodDimmingScrim(scrimOpacity);
5665        }
5666
5667        public void dispatchDoubleTap(float viewX, float viewY) {
5668            dispatchTap(mAmbientIndicationContainer, viewX, viewY);
5669            dispatchTap(mAmbientIndicationContainer, viewX, viewY);
5670        }
5671
5672        private void dispatchTap(View view, float x, float y) {
5673            long now = SystemClock.elapsedRealtime();
5674            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
5675            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
5676        }
5677
5678        private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
5679            MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
5680            view.dispatchTouchEvent(ev);
5681            ev.recycle();
5682        }
5683
5684        private boolean shouldAnimateWakeup() {
5685            return mAnimateWakeup;
5686        }
5687    }
5688
5689    public boolean shouldIgnoreTouch() {
5690        return isDozing() && mDozeServiceHost.mIgnoreTouchWhilePulsing;
5691    }
5692
5693    // Begin Extra BaseStatusBar methods.
5694
5695    protected CommandQueue mCommandQueue;
5696    protected IStatusBarService mBarService;
5697
5698    // all notifications
5699    protected NotificationData mNotificationData;
5700    protected NotificationStackScrollLayout mStackScroller;
5701
5702    protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
5703
5704    protected RemoteInputController mRemoteInputController;
5705
5706    // for heads up notifications
5707    protected HeadsUpManager mHeadsUpManager;
5708
5709    private AboveShelfObserver mAboveShelfObserver;
5710
5711    // handling reordering
5712    protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
5713
5714    protected int mCurrentUserId = 0;
5715    final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
5716
5717    protected int mLayoutDirection = -1; // invalid
5718    protected AccessibilityManager mAccessibilityManager;
5719
5720    protected boolean mDeviceInteractive;
5721
5722    protected boolean mVisible;
5723    protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
5724    protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
5725
5726    /**
5727     * Notifications with keys in this set are not actually around anymore. We kept them around
5728     * when they were canceled in response to a remote input interaction. This allows us to show
5729     * what you replied and allows you to continue typing into it.
5730     */
5731    protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
5732
5733    // mScreenOnFromKeyguard && mVisible.
5734    private boolean mVisibleToUser;
5735
5736    private Locale mLocale;
5737
5738    protected boolean mUseHeadsUp = false;
5739    protected boolean mHeadsUpTicker = false;
5740    protected boolean mDisableNotificationAlerts = false;
5741
5742    protected DevicePolicyManager mDevicePolicyManager;
5743    protected PowerManager mPowerManager;
5744    protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
5745
5746    // public mode, private notifications, etc
5747    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
5748    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
5749    private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
5750
5751    private UserManager mUserManager;
5752
5753    protected KeyguardManager mKeyguardManager;
5754    private LockPatternUtils mLockPatternUtils;
5755    private DeviceProvisionedController mDeviceProvisionedController
5756            = Dependency.get(DeviceProvisionedController.class);
5757    protected SystemServicesProxy mSystemServicesProxy;
5758
5759    // UI-specific methods
5760
5761    protected WindowManager mWindowManager;
5762    protected IWindowManager mWindowManagerService;
5763
5764    protected Display mDisplay;
5765
5766    protected RecentsComponent mRecents;
5767
5768    protected int mZenMode;
5769
5770    // which notification is currently being longpress-examined by the user
5771    private NotificationGuts mNotificationGutsExposed;
5772    private MenuItem mGutsMenuItem;
5773
5774    private KeyboardShortcuts mKeyboardShortcuts;
5775
5776    protected NotificationShelf mNotificationShelf;
5777    protected DismissView mDismissView;
5778    protected EmptyShadeView mEmptyShadeView;
5779
5780    private NotificationClicker mNotificationClicker = new NotificationClicker();
5781
5782    protected AssistManager mAssistManager;
5783
5784    protected boolean mVrMode;
5785
5786    private Set<String> mNonBlockablePkgs;
5787
5788    public boolean isDeviceInteractive() {
5789        return mDeviceInteractive;
5790    }
5791
5792    @Override  // NotificationData.Environment
5793    public boolean isDeviceProvisioned() {
5794        return mDeviceProvisionedController.isDeviceProvisioned();
5795    }
5796
5797    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
5798        @Override
5799        public void onVrStateChanged(boolean enabled) {
5800            mVrMode = enabled;
5801        }
5802    };
5803
5804    public boolean isDeviceInVrMode() {
5805        return mVrMode;
5806    }
5807
5808    private final DeviceProvisionedListener mDeviceProvisionedListener =
5809            new DeviceProvisionedListener() {
5810        @Override
5811        public void onDeviceProvisionedChanged() {
5812            updateNotifications();
5813        }
5814    };
5815
5816    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
5817        @Override
5818        public void onChange(boolean selfChange) {
5819            final int mode = Settings.Global.getInt(mContext.getContentResolver(),
5820                    Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
5821            setZenMode(mode);
5822
5823            updateLockscreenNotificationSetting();
5824        }
5825    };
5826
5827    private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
5828        @Override
5829        public void onChange(boolean selfChange) {
5830            // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
5831            // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
5832            mUsersAllowingPrivateNotifications.clear();
5833            mUsersAllowingNotifications.clear();
5834            // ... and refresh all the notifications
5835            updateLockscreenNotificationSetting();
5836            updateNotifications();
5837        }
5838    };
5839
5840    private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
5841
5842        @Override
5843        public boolean onClickHandler(
5844                final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
5845            wakeUpIfDozing(SystemClock.uptimeMillis(), view);
5846
5847
5848            if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
5849                return true;
5850            }
5851
5852            if (DEBUG) {
5853                Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
5854            }
5855            logActionClick(view);
5856            // The intent we are sending is for the application, which
5857            // won't have permission to immediately start an activity after
5858            // the user switches to home.  We know it is safe to do at this
5859            // point, so make sure new activity switches are now allowed.
5860            try {
5861                ActivityManager.getService().resumeAppSwitches();
5862            } catch (RemoteException e) {
5863            }
5864            final boolean isActivity = pendingIntent.isActivity();
5865            if (isActivity) {
5866                final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
5867                final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
5868                        mContext, pendingIntent.getIntent(), mCurrentUserId);
5869                dismissKeyguardThenExecute(new OnDismissAction() {
5870                    @Override
5871                    public boolean onDismiss() {
5872                        try {
5873                            ActivityManager.getService().resumeAppSwitches();
5874                        } catch (RemoteException e) {
5875                        }
5876
5877                        boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
5878
5879                        // close the shade if it was open
5880                        if (handled && !mNotificationPanel.isFullyCollapsed()) {
5881                            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
5882                                    true /* force */);
5883                            visibilityChanged(false);
5884                            mAssistManager.hideAssist();
5885
5886                            // Wait for activity start.
5887                            return true;
5888                        } else {
5889                            return false;
5890                        }
5891
5892                    }
5893                }, afterKeyguardGone);
5894                return true;
5895            } else {
5896                return superOnClickHandler(view, pendingIntent, fillInIntent);
5897            }
5898        }
5899
5900        private void logActionClick(View view) {
5901            ViewParent parent = view.getParent();
5902            String key = getNotificationKeyForParent(parent);
5903            if (key == null) {
5904                Log.w(TAG, "Couldn't determine notification for click.");
5905                return;
5906            }
5907            int index = -1;
5908            // If this is a default template, determine the index of the button.
5909            if (view.getId() == com.android.internal.R.id.action0 &&
5910                    parent != null && parent instanceof ViewGroup) {
5911                ViewGroup actionGroup = (ViewGroup) parent;
5912                index = actionGroup.indexOfChild(view);
5913            }
5914            try {
5915                mBarService.onNotificationActionClick(key, index);
5916            } catch (RemoteException e) {
5917                // Ignore
5918            }
5919        }
5920
5921        private String getNotificationKeyForParent(ViewParent parent) {
5922            while (parent != null) {
5923                if (parent instanceof ExpandableNotificationRow) {
5924                    return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
5925                }
5926                parent = parent.getParent();
5927            }
5928            return null;
5929        }
5930
5931        private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
5932                Intent fillInIntent) {
5933            return super.onClickHandler(view, pendingIntent, fillInIntent,
5934                    StackId.FULLSCREEN_WORKSPACE_STACK_ID);
5935        }
5936
5937        private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
5938            Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
5939            RemoteInput[] inputs = null;
5940            if (tag instanceof RemoteInput[]) {
5941                inputs = (RemoteInput[]) tag;
5942            }
5943
5944            if (inputs == null) {
5945                return false;
5946            }
5947
5948            RemoteInput input = null;
5949
5950            for (RemoteInput i : inputs) {
5951                if (i.getAllowFreeFormInput()) {
5952                    input = i;
5953                }
5954            }
5955
5956            if (input == null) {
5957                return false;
5958            }
5959
5960            ViewParent p = view.getParent();
5961            RemoteInputView riv = null;
5962            while (p != null) {
5963                if (p instanceof View) {
5964                    View pv = (View) p;
5965                    if (pv.isRootNamespace()) {
5966                        riv = findRemoteInputView(pv);
5967                        break;
5968                    }
5969                }
5970                p = p.getParent();
5971            }
5972            ExpandableNotificationRow row = null;
5973            while (p != null) {
5974                if (p instanceof ExpandableNotificationRow) {
5975                    row = (ExpandableNotificationRow) p;
5976                    break;
5977                }
5978                p = p.getParent();
5979            }
5980
5981            if (row == null) {
5982                return false;
5983            }
5984
5985            row.setUserExpanded(true);
5986
5987            if (!mAllowLockscreenRemoteInput) {
5988                final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
5989                if (isLockscreenPublicMode(userId)) {
5990                    onLockedRemoteInput(row, view);
5991                    return true;
5992                }
5993                if (mUserManager.getUserInfo(userId).isManagedProfile()
5994                        && mKeyguardManager.isDeviceLocked(userId)) {
5995                    onLockedWorkRemoteInput(userId, row, view);
5996                    return true;
5997                }
5998            }
5999
6000            if (riv == null) {
6001                riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
6002                if (riv == null) {
6003                    return false;
6004                }
6005                if (!row.getPrivateLayout().getExpandedChild().isShown()) {
6006                    onMakeExpandedVisibleForRemoteInput(row, view);
6007                    return true;
6008                }
6009            }
6010
6011            int width = view.getWidth();
6012            if (view instanceof TextView) {
6013                // Center the reveal on the text which might be off-center from the TextView
6014                TextView tv = (TextView) view;
6015                if (tv.getLayout() != null) {
6016                    int innerWidth = (int) tv.getLayout().getLineWidth(0);
6017                    innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
6018                    width = Math.min(width, innerWidth);
6019                }
6020            }
6021            int cx = view.getLeft() + width / 2;
6022            int cy = view.getTop() + view.getHeight() / 2;
6023            int w = riv.getWidth();
6024            int h = riv.getHeight();
6025            int r = Math.max(
6026                    Math.max(cx + cy, cx + (h - cy)),
6027                    Math.max((w - cx) + cy, (w - cx) + (h - cy)));
6028
6029            riv.setRevealParameters(cx, cy, r);
6030            riv.setPendingIntent(pendingIntent);
6031            riv.setRemoteInput(inputs, input);
6032            riv.focusAnimated();
6033
6034            return true;
6035        }
6036
6037        private RemoteInputView findRemoteInputView(View v) {
6038            if (v == null) {
6039                return null;
6040            }
6041            return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
6042        }
6043    };
6044
6045    private final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
6046        @Override
6047        public void onReceive(Context context, Intent intent) {
6048            String action = intent.getAction();
6049            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
6050                mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
6051                updateCurrentProfilesCache();
6052                if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
6053
6054                updateLockscreenNotificationSetting();
6055
6056                userSwitched(mCurrentUserId);
6057            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
6058                updateCurrentProfilesCache();
6059            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
6060                List<ActivityManager.RecentTaskInfo> recentTask = null;
6061                try {
6062                    recentTask = ActivityManager.getService().getRecentTasks(1,
6063                            ActivityManager.RECENT_WITH_EXCLUDED
6064                            | ActivityManager.RECENT_INCLUDE_PROFILES,
6065                            mCurrentUserId).getList();
6066                } catch (RemoteException e) {
6067                    // Abandon hope activity manager not running.
6068                }
6069                if (recentTask != null && recentTask.size() > 0) {
6070                    UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
6071                    if (user != null && user.isManagedProfile()) {
6072                        Toast toast = Toast.makeText(mContext,
6073                                R.string.managed_profile_foreground_toast,
6074                                Toast.LENGTH_SHORT);
6075                        TextView text = (TextView) toast.getView().findViewById(
6076                                android.R.id.message);
6077                        text.setCompoundDrawablesRelativeWithIntrinsicBounds(
6078                                R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
6079                        int paddingPx = mContext.getResources().getDimensionPixelSize(
6080                                R.dimen.managed_profile_toast_padding);
6081                        text.setCompoundDrawablePadding(paddingPx);
6082                        toast.show();
6083                    }
6084                }
6085            } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
6086                NotificationManager noMan = (NotificationManager)
6087                        mContext.getSystemService(Context.NOTIFICATION_SERVICE);
6088                noMan.cancel(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS);
6089
6090                Settings.Secure.putInt(mContext.getContentResolver(),
6091                        Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
6092                if (BANNER_ACTION_SETUP.equals(action)) {
6093                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
6094                            true /* force */);
6095                    mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
6096                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
6097
6098                    );
6099                }
6100            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
6101                final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
6102                final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
6103                if (intentSender != null) {
6104                    try {
6105                        mContext.startIntentSender(intentSender, null, 0, 0, 0);
6106                    } catch (IntentSender.SendIntentException e) {
6107                        /* ignore */
6108                    }
6109                }
6110                if (notificationKey != null) {
6111                    try {
6112                        mBarService.onNotificationClick(notificationKey);
6113                    } catch (RemoteException e) {
6114                        /* ignore */
6115                    }
6116                }
6117            }
6118        }
6119    };
6120
6121    private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
6122        @Override
6123        public void onReceive(Context context, Intent intent) {
6124            final String action = intent.getAction();
6125            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
6126
6127            if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
6128                    isCurrentProfile(getSendingUserId())) {
6129                mUsersAllowingPrivateNotifications.clear();
6130                updateLockscreenNotificationSetting();
6131                updateNotifications();
6132            } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
6133                if (userId != mCurrentUserId && isCurrentProfile(userId)) {
6134                    onWorkChallengeChanged();
6135                }
6136            }
6137        }
6138    };
6139
6140    private final NotificationListenerWithPlugins mNotificationListener =
6141            new NotificationListenerWithPlugins() {
6142        @Override
6143        public void onListenerConnected() {
6144            if (DEBUG) Log.d(TAG, "onListenerConnected");
6145            onPluginConnected();
6146            final StatusBarNotification[] notifications = getActiveNotifications();
6147            if (notifications == null) {
6148                Log.w(TAG, "onListenerConnected unable to get active notifications.");
6149                return;
6150            }
6151            final RankingMap currentRanking = getCurrentRanking();
6152            mHandler.post(new Runnable() {
6153                @Override
6154                public void run() {
6155                    for (StatusBarNotification sbn : notifications) {
6156                        try {
6157                            addNotification(sbn, currentRanking);
6158                        } catch (InflationException e) {
6159                            handleInflationException(sbn, e);
6160                        }
6161                    }
6162                }
6163            });
6164        }
6165
6166        @Override
6167        public void onNotificationPosted(final StatusBarNotification sbn,
6168                final RankingMap rankingMap) {
6169            if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
6170            if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
6171                mHandler.post(new Runnable() {
6172                    @Override
6173                    public void run() {
6174                        processForRemoteInput(sbn.getNotification());
6175                        String key = sbn.getKey();
6176                        mKeysKeptForRemoteInput.remove(key);
6177                        boolean isUpdate = mNotificationData.get(key) != null;
6178                        // In case we don't allow child notifications, we ignore children of
6179                        // notifications that have a summary, since we're not going to show them
6180                        // anyway. This is true also when the summary is canceled,
6181                        // because children are automatically canceled by NoMan in that case.
6182                        if (!ENABLE_CHILD_NOTIFICATIONS
6183                            && mGroupManager.isChildInGroupWithSummary(sbn)) {
6184                            if (DEBUG) {
6185                                Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
6186                            }
6187
6188                            // Remove existing notification to avoid stale data.
6189                            if (isUpdate) {
6190                                removeNotification(key, rankingMap);
6191                            } else {
6192                                mNotificationData.updateRanking(rankingMap);
6193                            }
6194                            return;
6195                        }
6196                        try {
6197                            if (isUpdate) {
6198                                updateNotification(sbn, rankingMap);
6199                            } else {
6200                                addNotification(sbn, rankingMap);
6201                            }
6202                        } catch (InflationException e) {
6203                            handleInflationException(sbn, e);
6204                        }
6205                    }
6206                });
6207            }
6208        }
6209
6210        @Override
6211        public void onNotificationRemoved(StatusBarNotification sbn,
6212                final RankingMap rankingMap) {
6213            if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
6214            if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
6215                final String key = sbn.getKey();
6216                mHandler.post(() -> removeNotification(key, rankingMap));
6217            }
6218        }
6219
6220        @Override
6221        public void onNotificationRankingUpdate(final RankingMap rankingMap) {
6222            if (DEBUG) Log.d(TAG, "onRankingUpdate");
6223            if (rankingMap != null) {
6224                RankingMap r = onPluginRankingUpdate(rankingMap);
6225                mHandler.post(() -> updateNotificationRanking(r));
6226            }
6227        }
6228
6229    };
6230
6231    private void updateCurrentProfilesCache() {
6232        synchronized (mCurrentProfiles) {
6233            mCurrentProfiles.clear();
6234            if (mUserManager != null) {
6235                for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
6236                    mCurrentProfiles.put(user.id, user);
6237                }
6238            }
6239        }
6240    }
6241
6242    protected void notifyUserAboutHiddenNotifications() {
6243        if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
6244                Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
6245            Log.d(TAG, "user hasn't seen notification about hidden notifications");
6246            if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
6247                Log.d(TAG, "insecure lockscreen, skipping notification");
6248                Settings.Secure.putInt(mContext.getContentResolver(),
6249                        Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
6250                return;
6251            }
6252            Log.d(TAG, "disabling lockecreen notifications and alerting the user");
6253            // disable lockscreen notifications until user acts on the banner.
6254            Settings.Secure.putInt(mContext.getContentResolver(),
6255                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
6256            Settings.Secure.putInt(mContext.getContentResolver(),
6257                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
6258
6259            final String packageName = mContext.getPackageName();
6260            PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
6261                    new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
6262                    PendingIntent.FLAG_CANCEL_CURRENT);
6263            PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
6264                    new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
6265                    PendingIntent.FLAG_CANCEL_CURRENT);
6266
6267            final int colorRes = com.android.internal.R.color.system_notification_accent_color;
6268            Notification.Builder note =
6269                    new Notification.Builder(mContext, NotificationChannels.GENERAL)
6270                            .setSmallIcon(R.drawable.ic_android)
6271                            .setContentTitle(mContext.getString(
6272                                    R.string.hidden_notifications_title))
6273                            .setContentText(mContext.getString(R.string.hidden_notifications_text))
6274                            .setOngoing(true)
6275                            .setColor(mContext.getColor(colorRes))
6276                            .setContentIntent(setupIntent)
6277                            .addAction(R.drawable.ic_close,
6278                                    mContext.getString(R.string.hidden_notifications_cancel),
6279                                    cancelIntent)
6280                            .addAction(R.drawable.ic_settings,
6281                                    mContext.getString(R.string.hidden_notifications_setup),
6282                                    setupIntent);
6283            overrideNotificationAppName(mContext, note);
6284
6285            NotificationManager noMan =
6286                    (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
6287            noMan.notify(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS, note.build());
6288        }
6289    }
6290
6291    @Override  // NotificationData.Environment
6292    public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
6293        final int thisUserId = mCurrentUserId;
6294        final int notificationUserId = n.getUserId();
6295        if (DEBUG && MULTIUSER_DEBUG) {
6296            Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
6297                    n, thisUserId, notificationUserId));
6298        }
6299        return isCurrentProfile(notificationUserId);
6300    }
6301
6302    protected void setNotificationShown(StatusBarNotification n) {
6303        setNotificationsShown(new String[]{n.getKey()});
6304    }
6305
6306    protected void setNotificationsShown(String[] keys) {
6307        try {
6308            mNotificationListener.setNotificationsShown(keys);
6309        } catch (RuntimeException e) {
6310            Log.d(TAG, "failed setNotificationsShown: ", e);
6311        }
6312    }
6313
6314    protected boolean isCurrentProfile(int userId) {
6315        synchronized (mCurrentProfiles) {
6316            return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
6317        }
6318    }
6319
6320    @Override
6321    public NotificationGroupManager getGroupManager() {
6322        return mGroupManager;
6323    }
6324
6325    public boolean isMediaNotification(NotificationData.Entry entry) {
6326        // TODO: confirm that there's a valid media key
6327        return entry.getExpandedContentView() != null &&
6328               entry.getExpandedContentView()
6329                       .findViewById(com.android.internal.R.id.media_actions) != null;
6330    }
6331
6332    // The button in the guts that links to the system notification settings for that app
6333    private void startAppNotificationSettingsActivity(String packageName, final int appUid,
6334            final NotificationChannel channel) {
6335        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
6336        intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
6337        intent.putExtra(Settings.EXTRA_APP_UID, appUid);
6338        if (channel != null) {
6339            intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
6340        }
6341        startNotificationGutsIntent(intent, appUid);
6342    }
6343
6344    private void startNotificationGutsIntent(final Intent intent, final int appUid) {
6345        dismissKeyguardThenExecute(new OnDismissAction() {
6346            @Override
6347            public boolean onDismiss() {
6348                AsyncTask.execute(new Runnable() {
6349                    @Override
6350                    public void run() {
6351                        TaskStackBuilder.create(mContext)
6352                                .addNextIntentWithParentStack(intent)
6353                                .startActivities(getActivityOptions(),
6354                                        new UserHandle(UserHandle.getUserId(appUid)));
6355                    }
6356                });
6357                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
6358                return true;
6359            }
6360        }, false /* afterKeyguardGone */);
6361    }
6362
6363    public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
6364        if (snoozeOption.getSnoozeCriterion() != null) {
6365            mNotificationListener.snoozeNotification(sbn.getKey(),
6366                    snoozeOption.getSnoozeCriterion().getId());
6367        } else {
6368            mNotificationListener.snoozeNotification(sbn.getKey(),
6369                    snoozeOption.getMinutesToSnoozeFor() * 60 * 1000);
6370        }
6371    }
6372
6373    private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
6374        row.inflateGuts();
6375        row.setGutsView(item);
6376        final StatusBarNotification sbn = row.getStatusBarNotification();
6377        row.setTag(sbn.getPackageName());
6378        final NotificationGuts guts = row.getGuts();
6379        guts.setClosedListener((NotificationGuts g) -> {
6380            if (!g.willBeRemoved() && !row.isRemoved()) {
6381                mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
6382            }
6383            if (mNotificationGutsExposed == g) {
6384                mNotificationGutsExposed = null;
6385                mGutsMenuItem = null;
6386            }
6387            String key = sbn.getKey();
6388            if (key.equals(mKeyToRemoveOnGutsClosed)) {
6389                mKeyToRemoveOnGutsClosed = null;
6390                removeNotification(key, mLatestRankingMap);
6391            }
6392        });
6393
6394        View gutsView = item.getGutsView();
6395        if (gutsView instanceof NotificationSnooze) {
6396            NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
6397            snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
6398            snoozeGuts.setStatusBarNotification(sbn);
6399            snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
6400            guts.setHeightChangedListener((NotificationGuts g) -> {
6401                mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
6402            });
6403        }
6404
6405        if (gutsView instanceof NotificationInfo) {
6406            final UserHandle userHandle = sbn.getUser();
6407            PackageManager pmUser = getPackageManagerForUser(mContext,
6408                    userHandle.getIdentifier());
6409            final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
6410                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
6411            final String pkg = sbn.getPackageName();
6412            NotificationInfo info = (NotificationInfo) gutsView;
6413            // Settings link is only valid for notifications that specify a user, unless this is the
6414            // system user.
6415            NotificationInfo.OnSettingsClickListener onSettingsClick = null;
6416            if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) {
6417                onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
6418                    mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
6419                    guts.resetFalsingCheck();
6420                    startAppNotificationSettingsActivity(pkg, appUid, channel);
6421                };
6422            }
6423            final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
6424                    Intent intent) -> {
6425                mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS);
6426                guts.resetFalsingCheck();
6427                startNotificationGutsIntent(intent, sbn.getUid());
6428            };
6429            final View.OnClickListener onDoneClick = (View v) -> {
6430                saveAndCloseNotificationMenu(info, row, guts, v);
6431            };
6432            final NotificationInfo.CheckSaveListener checkSaveListener =
6433                    (Runnable saveImportance) -> {
6434                // If the user has security enabled, show challenge if the setting is changed.
6435                if (isLockscreenPublicMode(userHandle.getIdentifier())
6436                        && (mState == StatusBarState.KEYGUARD
6437                                || mState == StatusBarState.SHADE_LOCKED)) {
6438                    onLockedNotificationImportanceChange(() -> {
6439                        saveImportance.run();
6440                        return true;
6441                    });
6442                } else {
6443                    saveImportance.run();
6444                }
6445            };
6446
6447            ArraySet<NotificationChannel> channels = new ArraySet<NotificationChannel>();
6448            channels.add(row.getEntry().channel);
6449            if (row.isSummaryWithChildren()) {
6450                // If this is a summary, then add in the children notification channels for the
6451                // same user and pkg.
6452                final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
6453                final int numChildren = childrenRows.size();
6454                for (int i = 0; i < numChildren; i++) {
6455                    final ExpandableNotificationRow childRow = childrenRows.get(i);
6456                    final NotificationChannel childChannel = childRow.getEntry().channel;
6457                    final StatusBarNotification childSbn = childRow.getStatusBarNotification();
6458                    if (childSbn.getUser().equals(userHandle) &&
6459                            childSbn.getPackageName().equals(pkg)) {
6460                        channels.add(childChannel);
6461                    }
6462                }
6463            }
6464            try {
6465                info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
6466                        row.getEntry().channel.getImportance(), sbn, onSettingsClick,
6467                        onAppSettingsClick, onDoneClick, checkSaveListener,
6468                        mNonBlockablePkgs);
6469            } catch (RemoteException e) {
6470                Log.e(TAG, e.toString());
6471            }
6472        }
6473    }
6474
6475    private void saveAndCloseNotificationMenu(NotificationInfo info,
6476            ExpandableNotificationRow row, NotificationGuts guts, View done) {
6477        guts.resetFalsingCheck();
6478        int[] rowLocation = new int[2];
6479        int[] doneLocation = new int[2];
6480        row.getLocationOnScreen(rowLocation);
6481        done.getLocationOnScreen(doneLocation);
6482
6483        final int centerX = done.getWidth() / 2;
6484        final int centerY = done.getHeight() / 2;
6485        final int x = doneLocation[0] - rowLocation[0] + centerX;
6486        final int y = doneLocation[1] - rowLocation[1] + centerY;
6487        closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
6488                true /* removeControls */, x, y, true /* resetMenu */);
6489    }
6490
6491    protected SwipeHelper.LongPressListener getNotificationLongClicker() {
6492        return new SwipeHelper.LongPressListener() {
6493            @Override
6494            public boolean onLongPress(View v, final int x, final int y,
6495                    MenuItem item) {
6496                if (!(v instanceof ExpandableNotificationRow)) {
6497                    return false;
6498                }
6499                if (v.getWindowToken() == null) {
6500                    Log.e(TAG, "Trying to show notification guts, but not attached to window");
6501                    return false;
6502                }
6503
6504                final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6505                if (row.isDark()) {
6506                    return false;
6507                }
6508                v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
6509                if (row.areGutsExposed()) {
6510                    closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
6511                            true /* removeControls */, -1 /* x */, -1 /* y */,
6512                            true /* resetMenu */);
6513                    return false;
6514                }
6515                bindGuts(row, item);
6516                NotificationGuts guts = row.getGuts();
6517
6518                // Assume we are a status_bar_notification_row
6519                if (guts == null) {
6520                    // This view has no guts. Examples are the more card or the dismiss all view
6521                    return false;
6522                }
6523
6524                mMetricsLogger.action(MetricsEvent.ACTION_NOTE_CONTROLS);
6525
6526                // ensure that it's laid but not visible until actually laid out
6527                guts.setVisibility(View.INVISIBLE);
6528                // Post to ensure the the guts are properly laid out.
6529                guts.post(new Runnable() {
6530                    @Override
6531                    public void run() {
6532                        if (row.getWindowToken() == null) {
6533                            Log.e(TAG, "Trying to show notification guts, but not attached to "
6534                                    + "window");
6535                            return;
6536                        }
6537                        closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
6538                                true /* removeControls */, -1 /* x */, -1 /* y */,
6539                                false /* resetMenu */);
6540                        guts.setVisibility(View.VISIBLE);
6541                        final double horz = Math.max(guts.getWidth() - x, x);
6542                        final double vert = Math.max(guts.getHeight() - y, y);
6543                        final float r = (float) Math.hypot(horz, vert);
6544                        final Animator a
6545                                = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
6546                        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
6547                        a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
6548                        a.addListener(new AnimatorListenerAdapter() {
6549                            @Override
6550                            public void onAnimationEnd(Animator animation) {
6551                                super.onAnimationEnd(animation);
6552                                // Move the notification view back over the menu
6553                                row.resetTranslation();
6554                            }
6555                        });
6556                        a.start();
6557                        final boolean needsFalsingProtection =
6558                                (mState == StatusBarState.KEYGUARD &&
6559                                !mAccessibilityManager.isTouchExplorationEnabled());
6560                        guts.setExposed(true /* exposed */, needsFalsingProtection);
6561                        row.closeRemoteInput();
6562                        mStackScroller.onHeightChanged(row, true /* needsAnimation */);
6563                        mNotificationGutsExposed = guts;
6564                        mGutsMenuItem = item;
6565                    }
6566                });
6567                return true;
6568            }
6569        };
6570    }
6571
6572    /**
6573     * Returns the exposed NotificationGuts or null if none are exposed.
6574     */
6575    public NotificationGuts getExposedGuts() {
6576        return mNotificationGutsExposed;
6577    }
6578
6579    /**
6580     * Closes guts or notification menus that might be visible and saves any changes.
6581     *
6582     * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
6583     * @param force true if guts should be closed regardless of state (used for snooze only).
6584     * @param removeControls true if controls (e.g. info) should be closed.
6585     * @param x if closed based on touch location, this is the x touch location.
6586     * @param y if closed based on touch location, this is the y touch location.
6587     * @param resetMenu if any notification menus that might be revealed should be closed.
6588     */
6589    public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
6590            int x, int y, boolean resetMenu) {
6591        if (mNotificationGutsExposed != null) {
6592            mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
6593        }
6594        if (resetMenu) {
6595            mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
6596        }
6597    }
6598
6599    @Override
6600    public void toggleSplitScreen() {
6601        toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
6602    }
6603
6604    @Override
6605    public void preloadRecentApps() {
6606        int msg = MSG_PRELOAD_RECENT_APPS;
6607        mHandler.removeMessages(msg);
6608        mHandler.sendEmptyMessage(msg);
6609    }
6610
6611    @Override
6612    public void cancelPreloadRecentApps() {
6613        int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
6614        mHandler.removeMessages(msg);
6615        mHandler.sendEmptyMessage(msg);
6616    }
6617
6618    @Override
6619    public void dismissKeyboardShortcutsMenu() {
6620        int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
6621        mHandler.removeMessages(msg);
6622        mHandler.sendEmptyMessage(msg);
6623    }
6624
6625    @Override
6626    public void toggleKeyboardShortcutsMenu(int deviceId) {
6627        int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
6628        mHandler.removeMessages(msg);
6629        mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
6630    }
6631
6632    @Override
6633    public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
6634        mTopHidesStatusBar = topAppHidesStatusBar;
6635        if (!topAppHidesStatusBar && mWereIconsJustHidden) {
6636            // Immediately update the icon hidden state, since that should only apply if we're
6637            // staying fullscreen.
6638            mWereIconsJustHidden = false;
6639            recomputeDisableFlags(true);
6640        }
6641        updateHideIconsForBouncer(true /* animate */);
6642    }
6643
6644    protected void sendCloseSystemWindows(String reason) {
6645        try {
6646            ActivityManager.getService().closeSystemDialogs(reason);
6647        } catch (RemoteException e) {
6648        }
6649    }
6650
6651    protected void toggleKeyboardShortcuts(int deviceId) {
6652        KeyboardShortcuts.toggle(mContext, deviceId);
6653    }
6654
6655    protected void dismissKeyboardShortcuts() {
6656        KeyboardShortcuts.dismiss();
6657    }
6658
6659    /**
6660     * Save the current "public" (locked and secure) state of the lockscreen.
6661     */
6662    public void setLockscreenPublicMode(boolean publicMode, int userId) {
6663        mLockscreenPublicMode.put(userId, publicMode);
6664    }
6665
6666    public boolean isLockscreenPublicMode(int userId) {
6667        if (userId == UserHandle.USER_ALL) {
6668            return mLockscreenPublicMode.get(mCurrentUserId, false);
6669        }
6670        return mLockscreenPublicMode.get(userId, false);
6671    }
6672
6673    /**
6674     * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
6675     * "public" (secure & locked) mode?
6676     */
6677    public boolean userAllowsNotificationsInPublic(int userHandle) {
6678        if (userHandle == UserHandle.USER_ALL) {
6679            return true;
6680        }
6681
6682        if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
6683            final boolean allowed = 0 != Settings.Secure.getIntForUser(
6684                    mContext.getContentResolver(),
6685                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
6686            mUsersAllowingNotifications.append(userHandle, allowed);
6687            return allowed;
6688        }
6689
6690        return mUsersAllowingNotifications.get(userHandle);
6691    }
6692
6693    /**
6694     * Has the given user chosen to allow their private (full) notifications to be shown even
6695     * when the lockscreen is in "public" (secure & locked) mode?
6696     */
6697    public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
6698        if (userHandle == UserHandle.USER_ALL) {
6699            return true;
6700        }
6701
6702        if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
6703            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
6704                    mContext.getContentResolver(),
6705                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
6706            final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
6707            final boolean allowed = allowedByUser && allowedByDpm;
6708            mUsersAllowingPrivateNotifications.append(userHandle, allowed);
6709            return allowed;
6710        }
6711
6712        return mUsersAllowingPrivateNotifications.get(userHandle);
6713    }
6714
6715    private boolean adminAllowsUnredactedNotifications(int userHandle) {
6716        if (userHandle == UserHandle.USER_ALL) {
6717            return true;
6718        }
6719        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
6720                    userHandle);
6721        return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
6722    }
6723
6724    /**
6725     * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
6726     * If so, notifications should be hidden.
6727     */
6728    @Override  // NotificationData.Environment
6729    public boolean shouldHideNotifications(int userId) {
6730        return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
6731                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
6732    }
6733
6734    /**
6735     * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
6736     * package-specific override.
6737     */
6738    @Override // NotificationDate.Environment
6739    public boolean shouldHideNotifications(String key) {
6740        return isLockscreenPublicMode(mCurrentUserId)
6741                && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
6742    }
6743
6744    /**
6745     * Returns true if we're on a secure lockscreen.
6746     */
6747    @Override  // NotificationData.Environment
6748    public boolean isSecurelyLocked(int userId) {
6749        return isLockscreenPublicMode(userId);
6750    }
6751
6752    public void onNotificationClear(StatusBarNotification notification) {
6753        try {
6754            mBarService.onNotificationClear(
6755                    notification.getPackageName(),
6756                    notification.getTag(),
6757                    notification.getId(),
6758                    notification.getUserId());
6759        } catch (android.os.RemoteException ex) {
6760            // oh well
6761        }
6762    }
6763
6764    /**
6765     * Called when the notification panel layouts
6766     */
6767    public void onPanelLaidOut() {
6768        updateKeyguardMaxNotifications();
6769    }
6770
6771    public void updateKeyguardMaxNotifications() {
6772        if (mState == StatusBarState.KEYGUARD) {
6773            // Since the number of notifications is determined based on the height of the view, we
6774            // need to update them.
6775            int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
6776            int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
6777            if (maxBefore != maxNotifications) {
6778                updateRowStates();
6779            }
6780        }
6781    }
6782
6783    protected void inflateViews(Entry entry, ViewGroup parent) {
6784        PackageManager pmUser = getPackageManagerForUser(mContext,
6785                entry.notification.getUser().getIdentifier());
6786
6787        final StatusBarNotification sbn = entry.notification;
6788        if (entry.row != null) {
6789            entry.reset();
6790            updateNotification(entry, pmUser, sbn, entry.row);
6791        } else {
6792            new RowInflaterTask().inflate(mContext, parent, entry,
6793                    row -> {
6794                        bindRow(entry, pmUser, sbn, row);
6795                        updateNotification(entry, pmUser, sbn, row);
6796                    });
6797        }
6798
6799    }
6800
6801    private void bindRow(Entry entry, PackageManager pmUser,
6802            StatusBarNotification sbn, ExpandableNotificationRow row) {
6803        row.setExpansionLogger(this, entry.notification.getKey());
6804        row.setGroupManager(mGroupManager);
6805        row.setHeadsUpManager(mHeadsUpManager);
6806        row.setAboveShelfChangedListener(mAboveShelfObserver);
6807        row.setRemoteInputController(mRemoteInputController);
6808        row.setOnExpandClickListener(this);
6809        row.setRemoteViewClickHandler(mOnClickHandler);
6810        row.setInflationCallback(this);
6811        row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
6812
6813        // Get the app name.
6814        // Note that Notification.Builder#bindHeaderAppName has similar logic
6815        // but since this field is used in the guts, it must be accurate.
6816        // Therefore we will only show the application label, or, failing that, the
6817        // package name. No substitutions.
6818        final String pkg = sbn.getPackageName();
6819        String appname = pkg;
6820        try {
6821            final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
6822                    PackageManager.MATCH_UNINSTALLED_PACKAGES
6823                            | PackageManager.MATCH_DISABLED_COMPONENTS);
6824            if (info != null) {
6825                appname = String.valueOf(pmUser.getApplicationLabel(info));
6826            }
6827        } catch (NameNotFoundException e) {
6828            // Do nothing
6829        }
6830        row.setAppName(appname);
6831        row.setOnDismissRunnable(() ->
6832                performRemoveNotification(row.getStatusBarNotification()));
6833        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
6834        if (ENABLE_REMOTE_INPUT) {
6835            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
6836        }
6837    }
6838
6839    private void updateNotification(Entry entry, PackageManager pmUser,
6840            StatusBarNotification sbn, ExpandableNotificationRow row) {
6841        row.setNeedsRedaction(needsRedaction(entry));
6842        boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
6843        boolean isUpdate = mNotificationData.get(entry.key) != null;
6844        boolean wasLowPriority = row.isLowPriority();
6845        row.setIsLowPriority(isLowPriority);
6846        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
6847        // bind the click event to the content area
6848        mNotificationClicker.register(row, sbn);
6849
6850        // Extract target SDK version.
6851        try {
6852            ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
6853            entry.targetSdk = info.targetSdkVersion;
6854        } catch (NameNotFoundException ex) {
6855            Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
6856        }
6857        row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
6858                && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
6859        entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
6860        entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
6861
6862        entry.row = row;
6863        entry.row.setOnActivatedListener(this);
6864
6865        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
6866                mNotificationData.getImportance(sbn.getKey()));
6867        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
6868        row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
6869        row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
6870        row.updateNotification(entry);
6871    }
6872
6873    /**
6874     * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
6875     * via first-class API.
6876     *
6877     * TODO: Remove once enough apps specify remote inputs on their own.
6878     */
6879    private void processForRemoteInput(Notification n) {
6880        if (!ENABLE_REMOTE_INPUT) return;
6881
6882        if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
6883                (n.actions == null || n.actions.length == 0)) {
6884            Notification.Action viableAction = null;
6885            Notification.WearableExtender we = new Notification.WearableExtender(n);
6886
6887            List<Notification.Action> actions = we.getActions();
6888            final int numActions = actions.size();
6889
6890            for (int i = 0; i < numActions; i++) {
6891                Notification.Action action = actions.get(i);
6892                if (action == null) {
6893                    continue;
6894                }
6895                RemoteInput[] remoteInputs = action.getRemoteInputs();
6896                if (remoteInputs == null) {
6897                    continue;
6898                }
6899                for (RemoteInput ri : remoteInputs) {
6900                    if (ri.getAllowFreeFormInput()) {
6901                        viableAction = action;
6902                        break;
6903                    }
6904                }
6905                if (viableAction != null) {
6906                    break;
6907                }
6908            }
6909
6910            if (viableAction != null) {
6911                Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
6912                rebuilder.setActions(viableAction);
6913                rebuilder.build(); // will rewrite n
6914            }
6915        }
6916    }
6917
6918    public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
6919        if (!isDeviceProvisioned()) return;
6920
6921        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
6922        final boolean afterKeyguardGone = intent.isActivity()
6923                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
6924                mCurrentUserId);
6925        dismissKeyguardThenExecute(new OnDismissAction() {
6926            @Override
6927            public boolean onDismiss() {
6928                new Thread() {
6929                    @Override
6930                    public void run() {
6931                        try {
6932                            // The intent we are sending is for the application, which
6933                            // won't have permission to immediately start an activity after
6934                            // the user switches to home.  We know it is safe to do at this
6935                            // point, so make sure new activity switches are now allowed.
6936                            ActivityManager.getService().resumeAppSwitches();
6937                        } catch (RemoteException e) {
6938                        }
6939                        try {
6940                            intent.send(null, 0, null, null, null, null, getActivityOptions());
6941                        } catch (PendingIntent.CanceledException e) {
6942                            // the stack trace isn't very helpful here.
6943                            // Just log the exception message.
6944                            Log.w(TAG, "Sending intent failed: " + e);
6945
6946                            // TODO: Dismiss Keyguard.
6947                        }
6948                        if (intent.isActivity()) {
6949                            mAssistManager.hideAssist();
6950                        }
6951                    }
6952                }.start();
6953
6954                if (!mNotificationPanel.isFullyCollapsed()) {
6955                    // close the shade if it was open
6956                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
6957                            true /* force */, true /* delayed */);
6958                    visibilityChanged(false);
6959
6960                    return true;
6961                } else {
6962                    return false;
6963                }
6964            }
6965        }, afterKeyguardGone);
6966    }
6967
6968
6969    private final class NotificationClicker implements View.OnClickListener {
6970
6971        @Override
6972        public void onClick(final View v) {
6973            if (!(v instanceof ExpandableNotificationRow)) {
6974                Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
6975                return;
6976            }
6977
6978            wakeUpIfDozing(SystemClock.uptimeMillis(), v);
6979
6980            final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
6981            final StatusBarNotification sbn = row.getStatusBarNotification();
6982            if (sbn == null) {
6983                Log.e(TAG, "NotificationClicker called on an unclickable notification,");
6984                return;
6985            }
6986
6987            // Check if the notification is displaying the menu, if so slide notification back
6988            if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
6989                row.animateTranslateNotification(0);
6990                return;
6991            }
6992
6993            Notification notification = sbn.getNotification();
6994            final PendingIntent intent = notification.contentIntent != null
6995                    ? notification.contentIntent
6996                    : notification.fullScreenIntent;
6997            final String notificationKey = sbn.getKey();
6998
6999            // Mark notification for one frame.
7000            row.setJustClicked(true);
7001            DejankUtils.postAfterTraversal(new Runnable() {
7002                @Override
7003                public void run() {
7004                    row.setJustClicked(false);
7005                }
7006            });
7007
7008            final boolean afterKeyguardGone = intent.isActivity()
7009                    && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
7010                            mCurrentUserId);
7011            dismissKeyguardThenExecute(new OnDismissAction() {
7012                @Override
7013                public boolean onDismiss() {
7014                    if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
7015                        // Release the HUN notification to the shade.
7016
7017                        if (isPanelFullyCollapsed()) {
7018                            HeadsUpManager.setIsClickedNotification(row, true);
7019                        }
7020                        //
7021                        // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
7022                        // become canceled shortly by NoMan, but we can't assume that.
7023                        mHeadsUpManager.releaseImmediately(notificationKey);
7024                    }
7025                    StatusBarNotification parentToCancel = null;
7026                    if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
7027                        StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
7028                                        .getStatusBarNotification();
7029                        if (shouldAutoCancel(summarySbn)) {
7030                            parentToCancel = summarySbn;
7031                        }
7032                    }
7033                    final StatusBarNotification parentToCancelFinal = parentToCancel;
7034                    final Runnable runnable = new Runnable() {
7035                        @Override
7036                        public void run() {
7037                            try {
7038                                // The intent we are sending is for the application, which
7039                                // won't have permission to immediately start an activity after
7040                                // the user switches to home.  We know it is safe to do at this
7041                                // point, so make sure new activity switches are now allowed.
7042                                ActivityManager.getService().resumeAppSwitches();
7043                            } catch (RemoteException e) {
7044                            }
7045                            if (intent != null) {
7046                                // If we are launching a work activity and require to launch
7047                                // separate work challenge, we defer the activity action and cancel
7048                                // notification until work challenge is unlocked.
7049                                if (intent.isActivity()) {
7050                                    final int userId = intent.getCreatorUserHandle()
7051                                            .getIdentifier();
7052                                    if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
7053                                            && mKeyguardManager.isDeviceLocked(userId)) {
7054                                        // TODO(b/28935539): should allow certain activities to
7055                                        // bypass work challenge
7056                                        if (startWorkChallengeIfNecessary(userId,
7057                                                intent.getIntentSender(), notificationKey)) {
7058                                            // Show work challenge, do not run PendingIntent and
7059                                            // remove notification
7060                                            return;
7061                                        }
7062                                    }
7063                                }
7064                                try {
7065                                    intent.send(null, 0, null, null, null, null,
7066                                            getActivityOptions());
7067                                } catch (PendingIntent.CanceledException e) {
7068                                    // the stack trace isn't very helpful here.
7069                                    // Just log the exception message.
7070                                    Log.w(TAG, "Sending contentIntent failed: " + e);
7071
7072                                    // TODO: Dismiss Keyguard.
7073                                }
7074                                if (intent.isActivity()) {
7075                                    mAssistManager.hideAssist();
7076                                }
7077                            }
7078
7079                            try {
7080                                mBarService.onNotificationClick(notificationKey);
7081                            } catch (RemoteException ex) {
7082                                // system process is dead if we're here.
7083                            }
7084                            if (parentToCancelFinal != null) {
7085                                // We have to post it to the UI thread for synchronization
7086                                mHandler.post(new Runnable() {
7087                                    @Override
7088                                    public void run() {
7089                                        Runnable removeRunnable = new Runnable() {
7090                                            @Override
7091                                            public void run() {
7092                                                performRemoveNotification(parentToCancelFinal);
7093                                            }
7094                                        };
7095                                        if (isCollapsing()) {
7096                                            // To avoid lags we're only performing the remove
7097                                            // after the shade was collapsed
7098                                            addPostCollapseAction(removeRunnable);
7099                                        } else {
7100                                            removeRunnable.run();
7101                                        }
7102                                    }
7103                                });
7104                            }
7105                        }
7106                    };
7107
7108                    if (mStatusBarKeyguardViewManager.isShowing()
7109                            && mStatusBarKeyguardViewManager.isOccluded()) {
7110                        mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
7111                    } else {
7112                        new Thread(runnable).start();
7113                    }
7114
7115                    if (!mNotificationPanel.isFullyCollapsed()) {
7116                        // close the shade if it was open
7117                        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
7118                                true /* force */, true /* delayed */);
7119                        visibilityChanged(false);
7120
7121                        return true;
7122                    } else {
7123                        return false;
7124                    }
7125                }
7126            }, afterKeyguardGone);
7127        }
7128
7129        private boolean shouldAutoCancel(StatusBarNotification sbn) {
7130            int flags = sbn.getNotification().flags;
7131            if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
7132                return false;
7133            }
7134            if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
7135                return false;
7136            }
7137            return true;
7138        }
7139
7140        public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
7141            Notification notification = sbn.getNotification();
7142            if (notification.contentIntent != null || notification.fullScreenIntent != null) {
7143                row.setOnClickListener(this);
7144            } else {
7145                row.setOnClickListener(null);
7146            }
7147        }
7148    }
7149
7150    protected Bundle getActivityOptions() {
7151        // Anything launched from the notification shade should always go into the
7152        // fullscreen stack.
7153        ActivityOptions options = ActivityOptions.makeBasic();
7154        options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
7155        return options.toBundle();
7156    }
7157
7158    protected void visibilityChanged(boolean visible) {
7159        if (mVisible != visible) {
7160            mVisible = visible;
7161            if (!visible) {
7162                closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
7163                        true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
7164            }
7165        }
7166        updateVisibleToUser();
7167    }
7168
7169    protected void updateVisibleToUser() {
7170        boolean oldVisibleToUser = mVisibleToUser;
7171        mVisibleToUser = mVisible && mDeviceInteractive;
7172
7173        if (oldVisibleToUser != mVisibleToUser) {
7174            handleVisibleToUserChanged(mVisibleToUser);
7175        }
7176    }
7177
7178    /**
7179     * Clear Buzz/Beep/Blink.
7180     */
7181    public void clearNotificationEffects() {
7182        try {
7183            mBarService.clearNotificationEffects();
7184        } catch (RemoteException e) {
7185            // Won't fail unless the world has ended.
7186        }
7187    }
7188
7189    /**
7190     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
7191     * about the failure.
7192     *
7193     * WARNING: this will call back into us.  Don't hold any locks.
7194     */
7195    void handleNotificationError(StatusBarNotification n, String message) {
7196        removeNotification(n.getKey(), null);
7197        try {
7198            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
7199                    n.getInitialPid(), message, n.getUserId());
7200        } catch (RemoteException ex) {
7201            // The end is nigh.
7202        }
7203    }
7204
7205    protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
7206        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
7207        if (entry == null) {
7208            Log.w(TAG, "removeNotification for unknown key: " + key);
7209            return null;
7210        }
7211        updateNotifications();
7212        Dependency.get(LeakDetector.class).trackGarbage(entry);
7213        return entry.notification;
7214    }
7215
7216    protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
7217            throws InflationException {
7218        if (DEBUG) {
7219            Log.d(TAG, "createNotificationViews(notification=" + sbn);
7220        }
7221        NotificationData.Entry entry = new NotificationData.Entry(sbn);
7222        Dependency.get(LeakDetector.class).trackInstance(entry);
7223        entry.createIcons(mContext, sbn);
7224        // Construct the expanded view.
7225        inflateViews(entry, mStackScroller);
7226        return entry;
7227    }
7228
7229    protected void addNotificationViews(Entry entry) {
7230        if (entry == null) {
7231            return;
7232        }
7233        // Add the expanded view and icon.
7234        mNotificationData.add(entry);
7235        updateNotifications();
7236    }
7237
7238    /**
7239     * Updates expanded, dimmed and locked states of notification rows.
7240     */
7241    protected void updateRowStates() {
7242        final int N = mStackScroller.getChildCount();
7243
7244        int visibleNotifications = 0;
7245        boolean onKeyguard = mState == StatusBarState.KEYGUARD;
7246        int maxNotifications = -1;
7247        if (onKeyguard) {
7248            maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
7249        }
7250        mStackScroller.setMaxDisplayedNotifications(maxNotifications);
7251        Stack<ExpandableNotificationRow> stack = new Stack<>();
7252        for (int i = N - 1; i >= 0; i--) {
7253            View child = mStackScroller.getChildAt(i);
7254            if (!(child instanceof ExpandableNotificationRow)) {
7255                continue;
7256            }
7257            stack.push((ExpandableNotificationRow) child);
7258        }
7259        while(!stack.isEmpty()) {
7260            ExpandableNotificationRow row = stack.pop();
7261            NotificationData.Entry entry = row.getEntry();
7262            boolean isChildNotification =
7263                    mGroupManager.isChildInGroupWithSummary(entry.notification);
7264
7265            row.setOnKeyguard(onKeyguard);
7266
7267            if (!onKeyguard) {
7268                // If mAlwaysExpandNonGroupedNotification is false, then only expand the
7269                // very first notification and if it's not a child of grouped notifications.
7270                row.setSystemExpanded(mAlwaysExpandNonGroupedNotification
7271                        || (visibleNotifications == 0 && !isChildNotification
7272                        && !row.isLowPriority()));
7273            }
7274
7275            entry.row.setShowAmbient(isDozing());
7276            int userId = entry.notification.getUserId();
7277            boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
7278                    entry.notification) && !entry.row.isRemoved();
7279            boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
7280            if (suppressedSummary
7281                    || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
7282                    || (onKeyguard && !showOnKeyguard)) {
7283                entry.row.setVisibility(View.GONE);
7284            } else {
7285                boolean wasGone = entry.row.getVisibility() == View.GONE;
7286                if (wasGone) {
7287                    entry.row.setVisibility(View.VISIBLE);
7288                }
7289                if (!isChildNotification && !entry.row.isRemoved()) {
7290                    if (wasGone) {
7291                        // notify the scroller of a child addition
7292                        mStackScroller.generateAddAnimation(entry.row,
7293                                !showOnKeyguard /* fromMoreCard */);
7294                    }
7295                    visibleNotifications++;
7296                }
7297            }
7298            if (row.isSummaryWithChildren()) {
7299                List<ExpandableNotificationRow> notificationChildren =
7300                        row.getNotificationChildren();
7301                int size = notificationChildren.size();
7302                for (int i = size - 1; i >= 0; i--) {
7303                    stack.push(notificationChildren.get(i));
7304                }
7305            }
7306        }
7307        mNotificationPanel.setNoVisibleNotifications(visibleNotifications == 0);
7308
7309        // The following views will be moved to the end of mStackScroller. This counter represents
7310        // the offset from the last child. Initialized to 1 for the very last position. It is post-
7311        // incremented in the following "changeViewPosition" calls so that its value is correct for
7312        // subsequent calls.
7313        int offsetFromEnd = 1;
7314        if (mDismissView != null) {
7315            mStackScroller.changeViewPosition(mDismissView,
7316                    mStackScroller.getChildCount() - offsetFromEnd++);
7317        }
7318
7319        mStackScroller.changeViewPosition(mEmptyShadeView,
7320                mStackScroller.getChildCount() - offsetFromEnd++);
7321
7322        // No post-increment for this call because it is the last one. Make sure to add one if
7323        // another "changeViewPosition" call is ever added.
7324        mStackScroller.changeViewPosition(mNotificationShelf,
7325                mStackScroller.getChildCount() - offsetFromEnd);
7326
7327        // Scrim opacity varies based on notification count
7328        mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount());
7329    }
7330
7331    public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
7332        return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
7333    }
7334
7335    // extended in StatusBar
7336    protected void setShowLockscreenNotifications(boolean show) {
7337        mShowLockscreenNotifications = show;
7338    }
7339
7340    protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
7341        mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
7342    }
7343
7344    private void updateLockscreenNotificationSetting() {
7345        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
7346                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
7347                1,
7348                mCurrentUserId) != 0;
7349        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
7350                null /* admin */, mCurrentUserId);
7351        final boolean allowedByDpm = (dpmFlags
7352                & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
7353
7354        setShowLockscreenNotifications(show && allowedByDpm);
7355
7356        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
7357            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
7358                    Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
7359                    0,
7360                    mCurrentUserId) != 0;
7361            final boolean remoteInputDpm =
7362                    (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
7363
7364            setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
7365        } else {
7366            setLockScreenAllowRemoteInput(false);
7367        }
7368    }
7369
7370    public void updateNotification(StatusBarNotification notification, RankingMap ranking)
7371            throws InflationException {
7372        if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
7373
7374        final String key = notification.getKey();
7375        abortExistingInflation(key);
7376        Entry entry = mNotificationData.get(key);
7377        if (entry == null) {
7378            return;
7379        }
7380        mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
7381        mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
7382        if (key.equals(mKeyToRemoveOnGutsClosed)) {
7383            mKeyToRemoveOnGutsClosed = null;
7384            Log.w(TAG, "Notification that was kept for guts was updated. " + key);
7385        }
7386
7387        Notification n = notification.getNotification();
7388        mNotificationData.updateRanking(ranking);
7389
7390        final StatusBarNotification oldNotification = entry.notification;
7391        entry.notification = notification;
7392        mGroupManager.onEntryUpdated(entry, oldNotification);
7393
7394        entry.updateIcons(mContext, notification);
7395        inflateViews(entry, mStackScroller);
7396
7397        mForegroundServiceController.updateNotification(notification,
7398                mNotificationData.getImportance(key));
7399
7400        boolean shouldPeek = shouldPeek(entry, notification);
7401        boolean alertAgain = alertAgain(entry, n);
7402
7403        updateHeadsUp(key, entry, shouldPeek, alertAgain);
7404        updateNotifications();
7405
7406        if (!notification.isClearable()) {
7407            // The user may have performed a dismiss action on the notification, since it's
7408            // not clearable we should snap it back.
7409            mStackScroller.snapViewIfNeeded(entry.row);
7410        }
7411
7412        if (DEBUG) {
7413            // Is this for you?
7414            boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
7415            Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
7416        }
7417
7418        setAreThereNotifications();
7419    }
7420
7421    protected void notifyHeadsUpGoingToSleep() {
7422        maybeEscalateHeadsUp();
7423    }
7424
7425    private boolean alertAgain(Entry oldEntry, Notification newNotification) {
7426        return oldEntry == null || !oldEntry.hasInterrupted()
7427                || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
7428    }
7429
7430    protected boolean shouldPeek(Entry entry) {
7431        return shouldPeek(entry, entry.notification);
7432    }
7433
7434    protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
7435        if (!mUseHeadsUp || isDeviceInVrMode()) {
7436            if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
7437            return false;
7438        }
7439
7440        if (mNotificationData.shouldFilterOut(sbn)) {
7441            if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
7442            return false;
7443        }
7444
7445        boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
7446
7447        if (!inUse && !isDozing()) {
7448            if (DEBUG) {
7449                Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
7450            }
7451            return false;
7452        }
7453
7454        if (!isDozing() && mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
7455            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
7456            return false;
7457        }
7458
7459        if (isDozing() && mNotificationData.shouldSuppressScreenOff(sbn.getKey())) {
7460            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
7461            return false;
7462        }
7463
7464        if (entry.hasJustLaunchedFullScreenIntent()) {
7465            if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
7466            return false;
7467        }
7468
7469        if (isSnoozedPackage(sbn)) {
7470            if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
7471            return false;
7472        }
7473
7474        // Allow peeking for DEFAULT notifications only if we're on Ambient Display.
7475        int importanceLevel = isDozing() ? NotificationManager.IMPORTANCE_DEFAULT
7476                : NotificationManager.IMPORTANCE_HIGH;
7477        if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) {
7478            if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
7479            return false;
7480        }
7481
7482        if (sbn.getNotification().fullScreenIntent != null) {
7483            if (mAccessibilityManager.isTouchExplorationEnabled()) {
7484                if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
7485                return false;
7486            } else if (mDozing) {
7487                // We never want heads up when we are dozing.
7488                return false;
7489            } else {
7490                // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
7491                return !mStatusBarKeyguardViewManager.isShowing()
7492                        || mStatusBarKeyguardViewManager.isOccluded();
7493            }
7494        }
7495
7496        // Don't peek notifications that are suppressed due to group alert behavior
7497        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
7498            if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
7499            return false;
7500        }
7501
7502        return true;
7503    }
7504
7505    /**
7506     * @return Whether the security bouncer from Keyguard is showing.
7507     */
7508    public boolean isBouncerShowing() {
7509        return mBouncerShowing;
7510    }
7511
7512    /**
7513     * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
7514     *         return PackageManager for mContext
7515     */
7516    public static PackageManager getPackageManagerForUser(Context context, int userId) {
7517        Context contextForUser = context;
7518        // UserHandle defines special userId as negative values, e.g. USER_ALL
7519        if (userId >= 0) {
7520            try {
7521                // Create a context for the correct user so if a package isn't installed
7522                // for user 0 we can still load information about the package.
7523                contextForUser =
7524                        context.createPackageContextAsUser(context.getPackageName(),
7525                        Context.CONTEXT_RESTRICTED,
7526                        new UserHandle(userId));
7527            } catch (NameNotFoundException e) {
7528                // Shouldn't fail to find the package name for system ui.
7529            }
7530        }
7531        return contextForUser.getPackageManager();
7532    }
7533
7534    @Override
7535    public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
7536        mUiOffloadThread.submit(() -> {
7537            try {
7538                mBarService.onNotificationExpansionChanged(key, userAction, expanded);
7539            } catch (RemoteException e) {
7540                // Ignore.
7541            }
7542        });
7543    }
7544
7545    public boolean isKeyguardSecure() {
7546        if (mStatusBarKeyguardViewManager == null) {
7547            // startKeyguard() hasn't been called yet, so we don't know.
7548            // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
7549            // value onVisibilityChanged().
7550            Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
7551                    new Throwable());
7552            return false;
7553        }
7554        return mStatusBarKeyguardViewManager.isSecure();
7555    }
7556
7557    @Override
7558    public void showAssistDisclosure() {
7559        if (mAssistManager != null) {
7560            mAssistManager.showDisclosure();
7561        }
7562    }
7563
7564    public NotificationPanelView getPanel() {
7565        return mNotificationPanel;
7566    }
7567
7568    @Override
7569    public void startAssist(Bundle args) {
7570        if (mAssistManager != null) {
7571            mAssistManager.startAssist(args);
7572        }
7573    }
7574    // End Extra BaseStatusBarMethods.
7575
7576    private final Runnable mAutoDim = () -> {
7577        if (mNavigationBar != null) {
7578            mNavigationBar.getBarTransitions().setAutoDim(true);
7579        }
7580    };
7581}
7582