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