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