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