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