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