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