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