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