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