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