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