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