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