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