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