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