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