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