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