1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.app.ActivityManager;
22 import android.app.ActivityManager.StackId;
23 import android.app.ActivityManagerNative;
24 import android.app.ActivityOptions;
25 import android.app.KeyguardManager;
26 import android.app.Notification;
27 import android.app.NotificationManager;
28 import android.app.PendingIntent;
29 import android.app.RemoteInput;
30 import android.app.TaskStackBuilder;
31 import android.app.admin.DevicePolicyManager;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.IntentSender;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManager.NameNotFoundException;
41 import android.content.pm.UserInfo;
42 import android.content.res.Configuration;
43 import android.content.res.Resources;
44 import android.database.ContentObserver;
45 import android.graphics.Rect;
46 import android.graphics.drawable.Drawable;
47 import android.graphics.drawable.Icon;
48 import android.os.AsyncTask;
49 import android.os.Build;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Message;
54 import android.os.PowerManager;
55 import android.os.RemoteException;
56 import android.os.ServiceManager;
57 import android.os.SystemProperties;
58 import android.os.UserHandle;
59 import android.os.UserManager;
60 import android.provider.Settings;
61 import android.service.dreams.DreamService;
62 import android.service.dreams.IDreamManager;
63 import android.service.notification.NotificationListenerService;
64 import android.service.notification.NotificationListenerService.RankingMap;
65 import android.service.notification.StatusBarNotification;
66 import android.service.vr.IVrManager;
67 import android.service.vr.IVrStateCallbacks;
68 import android.text.TextUtils;
69 import android.util.ArraySet;
70 import android.util.Log;
71 import android.util.Slog;
72 import android.util.SparseArray;
73 import android.util.SparseBooleanArray;
74 import android.view.Display;
75 import android.view.IWindowManager;
76 import android.view.LayoutInflater;
77 import android.view.MotionEvent;
78 import android.view.View;
79 import android.view.ViewAnimationUtils;
80 import android.view.ViewGroup;
81 import android.view.ViewParent;
82 import android.view.WindowManager;
83 import android.view.WindowManagerGlobal;
84 import android.view.accessibility.AccessibilityManager;
85 import android.widget.ImageView;
86 import android.widget.RemoteViews;
87 import android.widget.TextView;
88 import android.widget.Toast;
89 
90 import com.android.internal.logging.MetricsLogger;
91 import com.android.internal.logging.MetricsProto.MetricsEvent;
92 import com.android.internal.statusbar.IStatusBarService;
93 import com.android.internal.statusbar.StatusBarIcon;
94 import com.android.internal.widget.LockPatternUtils;
95 import com.android.keyguard.KeyguardHostView.OnDismissAction;
96 import com.android.keyguard.KeyguardUpdateMonitor;
97 import com.android.systemui.DejankUtils;
98 import com.android.systemui.Interpolators;
99 import com.android.systemui.R;
100 import com.android.systemui.RecentsComponent;
101 import com.android.systemui.SwipeHelper;
102 import com.android.systemui.SystemUI;
103 import com.android.systemui.assist.AssistManager;
104 import com.android.systemui.recents.Recents;
105 import com.android.systemui.statusbar.NotificationData.Entry;
106 import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
107 import com.android.systemui.statusbar.phone.NavigationBarView;
108 import com.android.systemui.statusbar.phone.NotificationGroupManager;
109 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
110 import com.android.systemui.statusbar.policy.HeadsUpManager;
111 import com.android.systemui.statusbar.policy.PreviewInflater;
112 import com.android.systemui.statusbar.policy.RemoteInputView;
113 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
114 import com.android.systemui.statusbar.stack.StackStateAnimator;
115 
116 import java.util.ArrayList;
117 import java.util.List;
118 import java.util.Locale;
119 
120 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
121 
122 public abstract class BaseStatusBar extends SystemUI implements
123         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
124         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
125         ExpandableNotificationRow.OnExpandClickListener,
126         OnGutsClosedListener {
127     public static final String TAG = "StatusBar";
128     public static final boolean DEBUG = false;
129     public static final boolean MULTIUSER_DEBUG = false;
130 
131     public static final boolean ENABLE_REMOTE_INPUT =
132             SystemProperties.getBoolean("debug.enable_remote_input", true);
133     public static final boolean ENABLE_CHILD_NOTIFICATIONS
134             = SystemProperties.getBoolean("debug.child_notifs", true);
135     public static final boolean FORCE_REMOTE_INPUT_HISTORY =
136             SystemProperties.getBoolean("debug.force_remoteinput_history", false);
137     private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
138 
139     protected static final int MSG_SHOW_RECENT_APPS = 1019;
140     protected static final int MSG_HIDE_RECENT_APPS = 1020;
141     protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
142     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
143     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
144     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
145     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
146     protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
147     protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
148 
149     protected static final boolean ENABLE_HEADS_UP = true;
150     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
151 
152     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
153 
154     // Should match the values in PhoneWindowManager
155     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
156     public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
157 
158     private static final String BANNER_ACTION_CANCEL =
159             "com.android.systemui.statusbar.banner_action_cancel";
160     private static final String BANNER_ACTION_SETUP =
161             "com.android.systemui.statusbar.banner_action_setup";
162     private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
163             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
164 
165     protected CommandQueue mCommandQueue;
166     protected IStatusBarService mBarService;
167     protected H mHandler = createHandler();
168 
169     // all notifications
170     protected NotificationData mNotificationData;
171     protected NotificationStackScrollLayout mStackScroller;
172 
173     protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
174 
175     protected RemoteInputController mRemoteInputController;
176 
177     // for heads up notifications
178     protected HeadsUpManager mHeadsUpManager;
179 
180     protected int mCurrentUserId = 0;
181     final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
182 
183     protected int mLayoutDirection = -1; // invalid
184     protected AccessibilityManager mAccessibilityManager;
185 
186     // on-screen navigation buttons
187     protected NavigationBarView mNavigationBarView = null;
188 
189     protected boolean mDeviceInteractive;
190 
191     protected boolean mVisible;
192     protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
193     protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
194 
195     /**
196      * Notifications with keys in this set are not actually around anymore. We kept them around
197      * when they were canceled in response to a remote input interaction. This allows us to show
198      * what you replied and allows you to continue typing into it.
199      */
200     protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
201 
202     // mScreenOnFromKeyguard && mVisible.
203     private boolean mVisibleToUser;
204 
205     private Locale mLocale;
206     private float mFontScale;
207 
208     protected boolean mUseHeadsUp = false;
209     protected boolean mHeadsUpTicker = false;
210     protected boolean mDisableNotificationAlerts = false;
211 
212     protected DevicePolicyManager mDevicePolicyManager;
213     protected IDreamManager mDreamManager;
214     protected PowerManager mPowerManager;
215     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
216 
217     // public mode, private notifications, etc
218     private boolean mLockscreenPublicMode = false;
219     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
220     private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
221 
222     private UserManager mUserManager;
223     private int mDensity;
224 
225     private KeyguardManager mKeyguardManager;
226     private LockPatternUtils mLockPatternUtils;
227 
228     // UI-specific methods
229 
230     /**
231      * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
232      * and add them to the window manager.
233      */
createAndAddWindows()234     protected abstract void createAndAddWindows();
235 
236     protected WindowManager mWindowManager;
237     protected IWindowManager mWindowManagerService;
238 
refreshLayout(int layoutDirection)239     protected abstract void refreshLayout(int layoutDirection);
240 
241     protected Display mDisplay;
242 
243     private boolean mDeviceProvisioned = false;
244 
245     protected RecentsComponent mRecents;
246 
247     protected int mZenMode;
248 
249     // which notification is currently being longpress-examined by the user
250     private NotificationGuts mNotificationGutsExposed;
251 
252     private KeyboardShortcuts mKeyboardShortcuts;
253 
254     /**
255      * The {@link StatusBarState} of the status bar.
256      */
257     protected int mState;
258     protected boolean mBouncerShowing;
259     protected boolean mShowLockscreenNotifications;
260     protected boolean mAllowLockscreenRemoteInput;
261 
262     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
263     protected DismissView mDismissView;
264     protected EmptyShadeView mEmptyShadeView;
265 
266     private NotificationClicker mNotificationClicker = new NotificationClicker();
267 
268     protected AssistManager mAssistManager;
269 
270     protected boolean mVrMode;
271 
272     @Override  // NotificationData.Environment
isDeviceProvisioned()273     public boolean isDeviceProvisioned() {
274         return mDeviceProvisioned;
275     }
276 
277     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
278         @Override
279         public void onVrStateChanged(boolean enabled) {
280             mVrMode = enabled;
281         }
282     };
283 
isDeviceInVrMode()284     public boolean isDeviceInVrMode() {
285         return mVrMode;
286     }
287 
288     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
289         @Override
290         public void onChange(boolean selfChange) {
291             final boolean provisioned = 0 != Settings.Global.getInt(
292                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
293             if (provisioned != mDeviceProvisioned) {
294                 mDeviceProvisioned = provisioned;
295                 updateNotifications();
296             }
297             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
298                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
299             setZenMode(mode);
300 
301             updateLockscreenNotificationSetting();
302         }
303     };
304 
305     private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
306         @Override
307         public void onChange(boolean selfChange) {
308             // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
309             // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
310             mUsersAllowingPrivateNotifications.clear();
311             mUsersAllowingNotifications.clear();
312             // ... and refresh all the notifications
313             updateNotifications();
314         }
315     };
316 
317     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
318         @Override
319         public boolean onClickHandler(
320                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
321             if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
322                 return true;
323             }
324 
325             if (DEBUG) {
326                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
327             }
328             logActionClick(view);
329             // The intent we are sending is for the application, which
330             // won't have permission to immediately start an activity after
331             // the user switches to home.  We know it is safe to do at this
332             // point, so make sure new activity switches are now allowed.
333             try {
334                 ActivityManagerNative.getDefault().resumeAppSwitches();
335             } catch (RemoteException e) {
336             }
337             final boolean isActivity = pendingIntent.isActivity();
338             if (isActivity) {
339                 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
340                 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
341                         mContext, pendingIntent.getIntent(), mCurrentUserId);
342                 dismissKeyguardThenExecute(new OnDismissAction() {
343                     @Override
344                     public boolean onDismiss() {
345                         if (keyguardShowing && !afterKeyguardGone) {
346                             try {
347                                 ActivityManagerNative.getDefault()
348                                         .keyguardWaitingForActivityDrawn();
349                                 ActivityManagerNative.getDefault().resumeAppSwitches();
350                             } catch (RemoteException e) {
351                             }
352                         }
353 
354                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
355                         overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
356 
357                         // close the shade if it was open
358                         if (handled) {
359                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
360                                     true /* force */);
361                             visibilityChanged(false);
362                             mAssistManager.hideAssist();
363                         }
364 
365                         // Wait for activity start.
366                         return handled;
367                     }
368                 }, afterKeyguardGone);
369                 return true;
370             } else {
371                 return superOnClickHandler(view, pendingIntent, fillInIntent);
372             }
373         }
374 
375         private void logActionClick(View view) {
376             ViewParent parent = view.getParent();
377             String key = getNotificationKeyForParent(parent);
378             if (key == null) {
379                 Log.w(TAG, "Couldn't determine notification for click.");
380                 return;
381             }
382             int index = -1;
383             // If this is a default template, determine the index of the button.
384             if (view.getId() == com.android.internal.R.id.action0 &&
385                     parent != null && parent instanceof ViewGroup) {
386                 ViewGroup actionGroup = (ViewGroup) parent;
387                 index = actionGroup.indexOfChild(view);
388             }
389             try {
390                 mBarService.onNotificationActionClick(key, index);
391             } catch (RemoteException e) {
392                 // Ignore
393             }
394         }
395 
396         private String getNotificationKeyForParent(ViewParent parent) {
397             while (parent != null) {
398                 if (parent instanceof ExpandableNotificationRow) {
399                     return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
400                 }
401                 parent = parent.getParent();
402             }
403             return null;
404         }
405 
406         private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
407                 Intent fillInIntent) {
408             return super.onClickHandler(view, pendingIntent, fillInIntent,
409                     StackId.FULLSCREEN_WORKSPACE_STACK_ID);
410         }
411 
412         private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
413             Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
414             RemoteInput[] inputs = null;
415             if (tag instanceof RemoteInput[]) {
416                 inputs = (RemoteInput[]) tag;
417             }
418 
419             if (inputs == null) {
420                 return false;
421             }
422 
423             RemoteInput input = null;
424 
425             for (RemoteInput i : inputs) {
426                 if (i.getAllowFreeFormInput()) {
427                     input = i;
428                 }
429             }
430 
431             if (input == null) {
432                 return false;
433             }
434 
435             ViewParent p = view.getParent();
436             RemoteInputView riv = null;
437             while (p != null) {
438                 if (p instanceof View) {
439                     View pv = (View) p;
440                     if (pv.isRootNamespace()) {
441                         riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
442                         break;
443                     }
444                 }
445                 p = p.getParent();
446             }
447             ExpandableNotificationRow row = null;
448             while (p != null) {
449                 if (p instanceof ExpandableNotificationRow) {
450                     row = (ExpandableNotificationRow) p;
451                     break;
452                 }
453                 p = p.getParent();
454             }
455 
456             if (riv == null || row == null) {
457                 return false;
458             }
459 
460             row.setUserExpanded(true);
461 
462             if (!mAllowLockscreenRemoteInput) {
463                 if (isLockscreenPublicMode()) {
464                     onLockedRemoteInput(row, view);
465                     return true;
466                 }
467                 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
468                 if (mUserManager.getUserInfo(userId).isManagedProfile()
469                         && mKeyguardManager.isDeviceLocked(userId)) {
470                     onLockedWorkRemoteInput(userId, row, view);
471                     return true;
472                 }
473             }
474 
475             riv.setVisibility(View.VISIBLE);
476             int cx = view.getLeft() + view.getWidth() / 2;
477             int cy = view.getTop() + view.getHeight() / 2;
478             int w = riv.getWidth();
479             int h = riv.getHeight();
480             int r = Math.max(
481                     Math.max(cx + cy, cx + (h - cy)),
482                     Math.max((w - cx) + cy, (w - cx) + (h - cy)));
483             ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r)
484                     .start();
485 
486             riv.setPendingIntent(pendingIntent);
487             riv.setRemoteInput(inputs, input);
488             riv.focus();
489 
490             return true;
491         }
492 
493     };
494 
495     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
496         @Override
497         public void onReceive(Context context, Intent intent) {
498             String action = intent.getAction();
499             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
500                 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
501                 updateCurrentProfilesCache();
502                 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
503 
504                 updateLockscreenNotificationSetting();
505 
506                 userSwitched(mCurrentUserId);
507             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
508                 updateCurrentProfilesCache();
509             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
510                 List<ActivityManager.RecentTaskInfo> recentTask = null;
511                 try {
512                     recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
513                             ActivityManager.RECENT_WITH_EXCLUDED
514                             | ActivityManager.RECENT_INCLUDE_PROFILES,
515                             mCurrentUserId).getList();
516                 } catch (RemoteException e) {
517                     // Abandon hope activity manager not running.
518                 }
519                 if (recentTask != null && recentTask.size() > 0) {
520                     UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
521                     if (user != null && user.isManagedProfile()) {
522                         Toast toast = Toast.makeText(mContext,
523                                 R.string.managed_profile_foreground_toast,
524                                 Toast.LENGTH_SHORT);
525                         TextView text = (TextView) toast.getView().findViewById(
526                                 android.R.id.message);
527                         text.setCompoundDrawablesRelativeWithIntrinsicBounds(
528                                 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
529                         int paddingPx = mContext.getResources().getDimensionPixelSize(
530                                 R.dimen.managed_profile_toast_padding);
531                         text.setCompoundDrawablePadding(paddingPx);
532                         toast.show();
533                     }
534                 }
535             } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
536                 NotificationManager noMan = (NotificationManager)
537                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
538                 noMan.cancel(R.id.notification_hidden);
539 
540                 Settings.Secure.putInt(mContext.getContentResolver(),
541                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
542                 if (BANNER_ACTION_SETUP.equals(action)) {
543                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
544                             true /* force */);
545                     mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
546                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
547 
548                     );
549                 }
550             } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
551                 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
552                 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
553                 if (intentSender != null) {
554                     try {
555                         mContext.startIntentSender(intentSender, null, 0, 0, 0);
556                     } catch (IntentSender.SendIntentException e) {
557                         /* ignore */
558                     }
559                 }
560                 if (notificationKey != null) {
561                     try {
562                         mBarService.onNotificationClick(notificationKey);
563                     } catch (RemoteException e) {
564                         /* ignore */
565                     }
566                 }
567                 onWorkChallengeUnlocked();
568             }
569         }
570     };
571 
572     private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
573         @Override
574         public void onReceive(Context context, Intent intent) {
575             String action = intent.getAction();
576             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
577                     isCurrentProfile(getSendingUserId())) {
578                 mUsersAllowingPrivateNotifications.clear();
579                 updateLockscreenNotificationSetting();
580                 updateNotifications();
581             }
582         }
583     };
584 
585     private final NotificationListenerService mNotificationListener =
586             new NotificationListenerService() {
587         @Override
588         public void onListenerConnected() {
589             if (DEBUG) Log.d(TAG, "onListenerConnected");
590             final StatusBarNotification[] notifications = getActiveNotifications();
591             final RankingMap currentRanking = getCurrentRanking();
592             mHandler.post(new Runnable() {
593                 @Override
594                 public void run() {
595                     for (StatusBarNotification sbn : notifications) {
596                         addNotification(sbn, currentRanking, null /* oldEntry */);
597                     }
598                 }
599             });
600         }
601 
602         @Override
603         public void onNotificationPosted(final StatusBarNotification sbn,
604                 final RankingMap rankingMap) {
605             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
606             if (sbn != null) {
607                 mHandler.post(new Runnable() {
608                     @Override
609                     public void run() {
610                         processForRemoteInput(sbn.getNotification());
611                         String key = sbn.getKey();
612                         mKeysKeptForRemoteInput.remove(key);
613                         boolean isUpdate = mNotificationData.get(key) != null;
614                         // In case we don't allow child notifications, we ignore children of
615                         // notifications that have a summary, since we're not going to show them
616                         // anyway. This is true also when the summary is canceled,
617                         // because children are automatically canceled by NoMan in that case.
618                         if (!ENABLE_CHILD_NOTIFICATIONS
619                             && mGroupManager.isChildInGroupWithSummary(sbn)) {
620                             if (DEBUG) {
621                                 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
622                             }
623 
624                             // Remove existing notification to avoid stale data.
625                             if (isUpdate) {
626                                 removeNotification(key, rankingMap);
627                             } else {
628                                 mNotificationData.updateRanking(rankingMap);
629                             }
630                             return;
631                         }
632                         if (isUpdate) {
633                             updateNotification(sbn, rankingMap);
634                         } else {
635                             addNotification(sbn, rankingMap, null /* oldEntry */);
636                         }
637                     }
638                 });
639             }
640         }
641 
642         @Override
643         public void onNotificationRemoved(StatusBarNotification sbn,
644                 final RankingMap rankingMap) {
645             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
646             if (sbn != null) {
647                 final String key = sbn.getKey();
648                 mHandler.post(new Runnable() {
649                     @Override
650                     public void run() {
651                         removeNotification(key, rankingMap);
652                     }
653                 });
654             }
655         }
656 
657         @Override
658         public void onNotificationRankingUpdate(final RankingMap rankingMap) {
659             if (DEBUG) Log.d(TAG, "onRankingUpdate");
660             if (rankingMap != null) {
661             mHandler.post(new Runnable() {
662                 @Override
663                 public void run() {
664                     updateNotificationRanking(rankingMap);
665                 }
666             });
667         }                            }
668 
669     };
670 
updateCurrentProfilesCache()671     private void updateCurrentProfilesCache() {
672         synchronized (mCurrentProfiles) {
673             mCurrentProfiles.clear();
674             if (mUserManager != null) {
675                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
676                     mCurrentProfiles.put(user.id, user);
677                 }
678             }
679         }
680     }
681 
start()682     public void start() {
683         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
684         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
685         mDisplay = mWindowManager.getDefaultDisplay();
686         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
687                 Context.DEVICE_POLICY_SERVICE);
688 
689         mNotificationData = new NotificationData(this);
690 
691         mAccessibilityManager = (AccessibilityManager)
692                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
693 
694         mDreamManager = IDreamManager.Stub.asInterface(
695                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
696         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
697 
698         mContext.getContentResolver().registerContentObserver(
699                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
700                 mSettingsObserver);
701         mContext.getContentResolver().registerContentObserver(
702                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
703                 mSettingsObserver);
704         mContext.getContentResolver().registerContentObserver(
705                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
706                 mSettingsObserver,
707                 UserHandle.USER_ALL);
708         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
709             mContext.getContentResolver().registerContentObserver(
710                     Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
711                     false,
712                     mSettingsObserver,
713                     UserHandle.USER_ALL);
714         }
715 
716         mContext.getContentResolver().registerContentObserver(
717                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
718                 true,
719                 mLockscreenSettingsObserver,
720                 UserHandle.USER_ALL);
721 
722         mBarService = IStatusBarService.Stub.asInterface(
723                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
724 
725         mRecents = getComponent(Recents.class);
726 
727         final Configuration currentConfig = mContext.getResources().getConfiguration();
728         mLocale = currentConfig.locale;
729         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
730         mFontScale = currentConfig.fontScale;
731         mDensity = currentConfig.densityDpi;
732 
733         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
734         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
735         mLockPatternUtils = new LockPatternUtils(mContext);
736 
737         // Connect in to the status bar manager service
738         mCommandQueue = new CommandQueue(this);
739 
740         int[] switches = new int[9];
741         ArrayList<IBinder> binders = new ArrayList<IBinder>();
742         ArrayList<String> iconSlots = new ArrayList<>();
743         ArrayList<StatusBarIcon> icons = new ArrayList<>();
744         Rect fullscreenStackBounds = new Rect();
745         Rect dockedStackBounds = new Rect();
746         try {
747             mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
748                     fullscreenStackBounds, dockedStackBounds);
749         } catch (RemoteException ex) {
750             // If the system process isn't there we're doomed anyway.
751         }
752 
753         createAndAddWindows();
754 
755         mSettingsObserver.onChange(false); // set up
756         disable(switches[0], switches[6], false /* animate */);
757         setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
758                 fullscreenStackBounds, dockedStackBounds);
759         topAppWindowChanged(switches[2] != 0);
760         // StatusBarManagerService has a back up of IME token and it's restored here.
761         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
762 
763         // Set up the initial icon state
764         int N = iconSlots.size();
765         int viewIndex = 0;
766         for (int i=0; i < N; i++) {
767             setIcon(iconSlots.get(i), icons.get(i));
768         }
769 
770         // Set up the initial notification state.
771         try {
772             mNotificationListener.registerAsSystemService(mContext,
773                     new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
774                     UserHandle.USER_ALL);
775         } catch (RemoteException e) {
776             Log.e(TAG, "Unable to register notification listener", e);
777         }
778 
779 
780         if (DEBUG) {
781             Log.d(TAG, String.format(
782                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
783                    icons.size(),
784                    switches[0],
785                    switches[1],
786                    switches[2],
787                    switches[3]
788                    ));
789         }
790 
791         mCurrentUserId = ActivityManager.getCurrentUser();
792         setHeadsUpUser(mCurrentUserId);
793 
794         IntentFilter filter = new IntentFilter();
795         filter.addAction(Intent.ACTION_USER_SWITCHED);
796         filter.addAction(Intent.ACTION_USER_ADDED);
797         filter.addAction(Intent.ACTION_USER_PRESENT);
798         mContext.registerReceiver(mBroadcastReceiver, filter);
799 
800         IntentFilter internalFilter = new IntentFilter();
801         internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
802         internalFilter.addAction(BANNER_ACTION_CANCEL);
803         internalFilter.addAction(BANNER_ACTION_SETUP);
804         mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
805 
806         IntentFilter allUsersFilter = new IntentFilter();
807         allUsersFilter.addAction(
808                 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
809         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
810                 null, null);
811         updateCurrentProfilesCache();
812 
813         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
814         try {
815             vrManager.registerListener(mVrStateCallbacks);
816         } catch (RemoteException e) {
817             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
818         }
819 
820     }
821 
notifyUserAboutHiddenNotifications()822     protected void notifyUserAboutHiddenNotifications() {
823         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
824                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
825             Log.d(TAG, "user hasn't seen notification about hidden notifications");
826             if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
827                 Log.d(TAG, "insecure lockscreen, skipping notification");
828                 Settings.Secure.putInt(mContext.getContentResolver(),
829                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
830                 return;
831             }
832             Log.d(TAG, "disabling lockecreen notifications and alerting the user");
833             // disable lockscreen notifications until user acts on the banner.
834             Settings.Secure.putInt(mContext.getContentResolver(),
835                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
836             Settings.Secure.putInt(mContext.getContentResolver(),
837                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
838 
839             final String packageName = mContext.getPackageName();
840             PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
841                     new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
842                     PendingIntent.FLAG_CANCEL_CURRENT);
843             PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
844                     new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
845                     PendingIntent.FLAG_CANCEL_CURRENT);
846 
847             final int colorRes = com.android.internal.R.color.system_notification_accent_color;
848             Notification.Builder note = new Notification.Builder(mContext)
849                     .setSmallIcon(R.drawable.ic_android)
850                     .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
851                     .setContentText(mContext.getString(R.string.hidden_notifications_text))
852                     .setPriority(Notification.PRIORITY_HIGH)
853                     .setOngoing(true)
854                     .setColor(mContext.getColor(colorRes))
855                     .setContentIntent(setupIntent)
856                     .addAction(R.drawable.ic_close,
857                             mContext.getString(R.string.hidden_notifications_cancel),
858                             cancelIntent)
859                     .addAction(R.drawable.ic_settings,
860                             mContext.getString(R.string.hidden_notifications_setup),
861                             setupIntent);
862             overrideNotificationAppName(mContext, note);
863 
864             NotificationManager noMan =
865                     (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
866             noMan.notify(R.id.notification_hidden, note.build());
867         }
868     }
869 
userSwitched(int newUserId)870     public void userSwitched(int newUserId) {
871         setHeadsUpUser(newUserId);
872     }
873 
setHeadsUpUser(int newUserId)874     protected abstract void setHeadsUpUser(int newUserId);
875 
876     @Override  // NotificationData.Environment
isNotificationForCurrentProfiles(StatusBarNotification n)877     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
878         final int thisUserId = mCurrentUserId;
879         final int notificationUserId = n.getUserId();
880         if (DEBUG && MULTIUSER_DEBUG) {
881             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
882                     n, thisUserId, notificationUserId));
883         }
884         return isCurrentProfile(notificationUserId);
885     }
886 
setNotificationShown(StatusBarNotification n)887     protected void setNotificationShown(StatusBarNotification n) {
888         setNotificationsShown(new String[]{n.getKey()});
889     }
890 
setNotificationsShown(String[] keys)891     protected void setNotificationsShown(String[] keys) {
892         try {
893             mNotificationListener.setNotificationsShown(keys);
894         } catch (RuntimeException e) {
895             Log.d(TAG, "failed setNotificationsShown: ", e);
896         }
897     }
898 
isCurrentProfile(int userId)899     protected boolean isCurrentProfile(int userId) {
900         synchronized (mCurrentProfiles) {
901             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
902         }
903     }
904 
905     @Override
getCurrentMediaNotificationKey()906     public String getCurrentMediaNotificationKey() {
907         return null;
908     }
909 
910     @Override
getGroupManager()911     public NotificationGroupManager getGroupManager() {
912         return mGroupManager;
913     }
914 
915     /**
916      * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
917      * @param action A dismiss action that is called if it's safe to start the activity.
918      * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
919      */
dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone)920     protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
921         action.onDismiss();
922     }
923 
924     @Override
onConfigurationChanged(Configuration newConfig)925     protected void onConfigurationChanged(Configuration newConfig) {
926         final Locale locale = mContext.getResources().getConfiguration().locale;
927         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
928         final float fontScale = newConfig.fontScale;
929         final int density = newConfig.densityDpi;
930         if (density != mDensity || mFontScale != fontScale) {
931             onDensityOrFontScaleChanged();
932             mDensity = density;
933             mFontScale = fontScale;
934         }
935         if (! locale.equals(mLocale) || ld != mLayoutDirection) {
936             if (DEBUG) {
937                 Log.v(TAG, String.format(
938                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
939                         locale, ld));
940             }
941             mLocale = locale;
942             mLayoutDirection = ld;
943             refreshLayout(ld);
944         }
945     }
946 
onDensityOrFontScaleChanged()947     protected void onDensityOrFontScaleChanged() {
948         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
949         for (int i = 0; i < activeNotifications.size(); i++) {
950             Entry entry = activeNotifications.get(i);
951             boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
952             entry.row.reInflateViews();
953             if (exposedGuts) {
954                 mNotificationGutsExposed = entry.row.getGuts();
955                 bindGuts(entry.row);
956             }
957             entry.cacheContentViews(mContext, null /* updatedNotification */);
958             inflateViews(entry, mStackScroller);
959         }
960     }
961 
bindVetoButtonClickListener(View row, final StatusBarNotification n)962     protected View bindVetoButtonClickListener(View row, final StatusBarNotification n) {
963         View vetoButton = row.findViewById(R.id.veto);
964         vetoButton.setOnClickListener(new View.OnClickListener() {
965             public void onClick(View v) {
966                 // Accessibility feedback
967                 v.announceForAccessibility(
968                         mContext.getString(R.string.accessibility_notification_dismissed));
969                 performRemoveNotification(n, false /* removeView */);
970             }
971         });
972         vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
973         return vetoButton;
974     }
975 
performRemoveNotification(StatusBarNotification n, boolean removeView)976     protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
977         final String pkg = n.getPackageName();
978         final String tag = n.getTag();
979         final int id = n.getId();
980         final int userId = n.getUserId();
981         try {
982             mBarService.onNotificationClear(pkg, tag, id, userId);
983             if (FORCE_REMOTE_INPUT_HISTORY
984                     && mKeysKeptForRemoteInput.contains(n.getKey())) {
985                 mKeysKeptForRemoteInput.remove(n.getKey());
986                 removeView = true;
987             }
988             if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
989                 removeView = true;
990             }
991             if (removeView) {
992                 removeNotification(n.getKey(), null);
993             }
994 
995         } catch (RemoteException ex) {
996             // system process is dead if we're here.
997         }
998     }
999 
1000 
applyColorsAndBackgrounds(StatusBarNotification sbn, NotificationData.Entry entry)1001     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
1002             NotificationData.Entry entry) {
1003 
1004         if (entry.getContentView().getId()
1005                 != com.android.internal.R.id.status_bar_latest_event_content) {
1006             // Using custom RemoteViews
1007             if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
1008                     && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
1009                 entry.row.setShowingLegacyBackground(true);
1010                 entry.legacy = true;
1011             }
1012         }
1013 
1014         if (entry.icon != null) {
1015             entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
1016         }
1017     }
1018 
isMediaNotification(NotificationData.Entry entry)1019     public boolean isMediaNotification(NotificationData.Entry entry) {
1020         // TODO: confirm that there's a valid media key
1021         return entry.getExpandedContentView() != null &&
1022                entry.getExpandedContentView()
1023                        .findViewById(com.android.internal.R.id.media_actions) != null;
1024     }
1025 
1026     // The (i) button in the guts that links to the system notification settings for that app
startAppNotificationSettingsActivity(String packageName, final int appUid)1027     private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
1028         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
1029         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
1030         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
1031         startNotificationGutsIntent(intent, appUid);
1032     }
1033 
startNotificationGutsIntent(final Intent intent, final int appUid)1034     private void startNotificationGutsIntent(final Intent intent, final int appUid) {
1035         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1036         dismissKeyguardThenExecute(new OnDismissAction() {
1037             @Override
1038             public boolean onDismiss() {
1039                 AsyncTask.execute(new Runnable() {
1040                     public void run() {
1041                         try {
1042                             if (keyguardShowing) {
1043                                 ActivityManagerNative.getDefault()
1044                                         .keyguardWaitingForActivityDrawn();
1045                             }
1046                             TaskStackBuilder.create(mContext)
1047                                     .addNextIntentWithParentStack(intent)
1048                                     .startActivities(getActivityOptions(),
1049                                             new UserHandle(UserHandle.getUserId(appUid)));
1050                             overrideActivityPendingAppTransition(keyguardShowing);
1051                         } catch (RemoteException e) {
1052                         }
1053                     }
1054                 });
1055                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
1056                 return true;
1057             }
1058         }, false /* afterKeyguardGone */);
1059     }
1060 
bindGuts(final ExpandableNotificationRow row)1061     private void bindGuts(final ExpandableNotificationRow row) {
1062         row.inflateGuts();
1063         final StatusBarNotification sbn = row.getStatusBarNotification();
1064         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
1065         row.setTag(sbn.getPackageName());
1066         final NotificationGuts guts = row.getGuts();
1067         guts.setClosedListener(this);
1068         final String pkg = sbn.getPackageName();
1069         String appname = pkg;
1070         Drawable pkgicon = null;
1071         int appUid = -1;
1072         try {
1073             final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1074                     PackageManager.GET_UNINSTALLED_PACKAGES
1075                             | PackageManager.GET_DISABLED_COMPONENTS);
1076             if (info != null) {
1077                 appname = String.valueOf(pmUser.getApplicationLabel(info));
1078                 pkgicon = pmUser.getApplicationIcon(info);
1079                 appUid = info.uid;
1080             }
1081         } catch (NameNotFoundException e) {
1082             // app is gone, just show package name and generic icon
1083             pkgicon = pmUser.getDefaultActivityIcon();
1084         }
1085 
1086         ((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
1087         ((TextView) guts.findViewById(R.id.pkgname)).setText(appname);
1088 
1089         final TextView settingsButton = (TextView) guts.findViewById(R.id.more_settings);
1090         if (appUid >= 0) {
1091             final int appUidF = appUid;
1092             settingsButton.setOnClickListener(new View.OnClickListener() {
1093                 public void onClick(View v) {
1094                     MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
1095                     guts.resetFalsingCheck();
1096                     startAppNotificationSettingsActivity(pkg, appUidF);
1097                 }
1098             });
1099             settingsButton.setText(R.string.notification_more_settings);
1100         } else {
1101             settingsButton.setVisibility(View.GONE);
1102         }
1103 
1104         guts.bindImportance(pmUser, sbn, mNotificationData.getImportance(sbn.getKey()));
1105 
1106         final TextView doneButton = (TextView) guts.findViewById(R.id.done);
1107         doneButton.setText(R.string.notification_done);
1108         doneButton.setOnClickListener(new View.OnClickListener() {
1109             @Override
1110             public void onClick(View v) {
1111                 // If the user has security enabled, show challenge if the setting is changed.
1112                 if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
1113                         (mState == StatusBarState.KEYGUARD
1114                         || mState == StatusBarState.SHADE_LOCKED)) {
1115                     OnDismissAction dismissAction = new OnDismissAction() {
1116                         @Override
1117                         public boolean onDismiss() {
1118                             saveImportanceCloseControls(sbn, row, guts, v);
1119                             return true;
1120                         }
1121                     };
1122                     onLockedNotificationImportanceChange(dismissAction);
1123                 } else {
1124                     saveImportanceCloseControls(sbn, row, guts, v);
1125                 }
1126             }
1127         });
1128     }
1129 
saveImportanceCloseControls(StatusBarNotification sbn, ExpandableNotificationRow row, NotificationGuts guts, View done)1130     private void saveImportanceCloseControls(StatusBarNotification sbn,
1131             ExpandableNotificationRow row, NotificationGuts guts, View done) {
1132         guts.resetFalsingCheck();
1133         guts.saveImportance(sbn);
1134 
1135         int[] rowLocation = new int[2];
1136         int[] doneLocation = new int[2];
1137         row.getLocationOnScreen(rowLocation);
1138         done.getLocationOnScreen(doneLocation);
1139 
1140         final int centerX = done.getWidth() / 2;
1141         final int centerY = done.getHeight() / 2;
1142         final int x = doneLocation[0] - rowLocation[0] + centerX;
1143         final int y = doneLocation[1] - rowLocation[1] + centerY;
1144         dismissPopups(x, y);
1145     }
1146 
getNotificationLongClicker()1147     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
1148         return new SwipeHelper.LongPressListener() {
1149             @Override
1150             public boolean onLongPress(View v, final int x, final int y) {
1151                 if (!(v instanceof ExpandableNotificationRow)) {
1152                     return false;
1153                 }
1154                 if (v.getWindowToken() == null) {
1155                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
1156                     return false;
1157                 }
1158 
1159                 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1160                 bindGuts(row);
1161 
1162                 // Assume we are a status_bar_notification_row
1163                 final NotificationGuts guts = row.getGuts();
1164                 if (guts == null) {
1165                     // This view has no guts. Examples are the more card or the dismiss all view
1166                     return false;
1167                 }
1168 
1169                 // Already showing?
1170                 if (guts.getVisibility() == View.VISIBLE) {
1171                     dismissPopups(x, y);
1172                     return false;
1173                 }
1174 
1175                 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
1176 
1177                 // ensure that it's laid but not visible until actually laid out
1178                 guts.setVisibility(View.INVISIBLE);
1179                 // Post to ensure the the guts are properly laid out.
1180                 guts.post(new Runnable() {
1181                     public void run() {
1182                         dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
1183                                 false /* animate */);
1184                         guts.setVisibility(View.VISIBLE);
1185                         final double horz = Math.max(guts.getWidth() - x, x);
1186                         final double vert = Math.max(guts.getHeight() - y, y);
1187                         final float r = (float) Math.hypot(horz, vert);
1188                         final Animator a
1189                                 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
1190                         a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1191                         a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1192                         a.addListener(new AnimatorListenerAdapter() {
1193                             @Override
1194                             public void onAnimationEnd(Animator animation) {
1195                                 super.onAnimationEnd(animation);
1196                                 // Move the notification view back over the gear
1197                                 row.resetTranslation();
1198                             }
1199                         });
1200                         a.start();
1201                         guts.setExposed(true /* exposed */,
1202                                 mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
1203                         row.closeRemoteInput();
1204                         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1205                         mNotificationGutsExposed = guts;
1206                     }
1207                 });
1208                 return true;
1209             }
1210         };
1211     }
1212 
1213     /**
1214      * Returns the exposed NotificationGuts or null if none are exposed.
1215      */
1216     public NotificationGuts getExposedGuts() {
1217         return mNotificationGutsExposed;
1218     }
1219 
1220     public void dismissPopups() {
1221         dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
1222     }
1223 
1224     private void dismissPopups(int x, int y) {
1225         dismissPopups(x, y, true /* resetGear */, false /* animate */);
1226     }
1227 
1228     public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
1229         if (mNotificationGutsExposed != null) {
1230             mNotificationGutsExposed.closeControls(x, y, true /* notify */);
1231         }
1232         if (resetGear) {
1233             mStackScroller.resetExposedGearView(animate, true /* force */);
1234         }
1235     }
1236 
1237     @Override
1238     public void onGutsClosed(NotificationGuts guts) {
1239         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1240         mNotificationGutsExposed = null;
1241     }
1242 
1243     @Override
1244     public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
1245         int msg = MSG_SHOW_RECENT_APPS;
1246         mHandler.removeMessages(msg);
1247         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0).sendToTarget();
1248     }
1249 
1250     @Override
1251     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1252         int msg = MSG_HIDE_RECENT_APPS;
1253         mHandler.removeMessages(msg);
1254         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1255                 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1256     }
1257 
1258     @Override
1259     public void toggleRecentApps() {
1260         toggleRecents();
1261     }
1262 
1263     @Override
1264     public void toggleSplitScreen() {
1265         toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
1266     }
1267 
1268     @Override
1269     public void preloadRecentApps() {
1270         int msg = MSG_PRELOAD_RECENT_APPS;
1271         mHandler.removeMessages(msg);
1272         mHandler.sendEmptyMessage(msg);
1273     }
1274 
1275     @Override
1276     public void cancelPreloadRecentApps() {
1277         int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1278         mHandler.removeMessages(msg);
1279         mHandler.sendEmptyMessage(msg);
1280     }
1281 
1282     @Override
1283     public void dismissKeyboardShortcutsMenu() {
1284         int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
1285         mHandler.removeMessages(msg);
1286         mHandler.sendEmptyMessage(msg);
1287     }
1288 
1289     @Override
1290     public void toggleKeyboardShortcutsMenu(int deviceId) {
1291         int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
1292         mHandler.removeMessages(msg);
1293         mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
1294     }
1295 
1296     /** Jumps to the next affiliated task in the group. */
1297     public void showNextAffiliatedTask() {
1298         int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1299         mHandler.removeMessages(msg);
1300         mHandler.sendEmptyMessage(msg);
1301     }
1302 
1303     /** Jumps to the previous affiliated task in the group. */
1304     public void showPreviousAffiliatedTask() {
1305         int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1306         mHandler.removeMessages(msg);
1307         mHandler.sendEmptyMessage(msg);
1308     }
1309 
1310     protected H createHandler() {
1311          return new H();
1312     }
1313 
1314     protected void sendCloseSystemWindows(String reason) {
1315         if (ActivityManagerNative.isSystemReady()) {
1316             try {
1317                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1318             } catch (RemoteException e) {
1319             }
1320         }
1321     }
1322 
1323     protected abstract View getStatusBarView();
1324 
1325     protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1326         // additional optimization when we have software system buttons - start loading the recent
1327         // tasks on touch down
1328         @Override
1329         public boolean onTouch(View v, MotionEvent event) {
1330             int action = event.getAction() & MotionEvent.ACTION_MASK;
1331             if (action == MotionEvent.ACTION_DOWN) {
1332                 preloadRecents();
1333             } else if (action == MotionEvent.ACTION_CANCEL) {
1334                 cancelPreloadingRecents();
1335             } else if (action == MotionEvent.ACTION_UP) {
1336                 if (!v.isPressed()) {
1337                     cancelPreloadingRecents();
1338                 }
1339 
1340             }
1341             return false;
1342         }
1343     };
1344 
1345     /**
1346      * Toggle docking the app window
1347      *
1348      * @param metricsDockAction the action to log when docking is successful, or -1 to not log
1349      *                          anything on successful docking
1350      * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when
1351      *                            undocking
1352      */
1353     protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
1354 
1355     /** Proxy for RecentsComponent */
1356 
1357     protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
1358         if (mRecents != null) {
1359             sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
1360             mRecents.showRecents(triggeredFromAltTab, fromHome);
1361         }
1362     }
1363 
1364     protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1365         if (mRecents != null) {
1366             mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1367         }
1368     }
1369 
1370     protected void toggleRecents() {
1371         if (mRecents != null) {
1372             mRecents.toggleRecents(mDisplay);
1373         }
1374     }
1375 
1376     protected void preloadRecents() {
1377         if (mRecents != null) {
1378             mRecents.preloadRecents();
1379         }
1380     }
1381 
1382     protected void toggleKeyboardShortcuts(int deviceId) {
1383         KeyboardShortcuts.toggle(mContext, deviceId);
1384     }
1385 
1386     protected void dismissKeyboardShortcuts() {
1387         KeyboardShortcuts.dismiss();
1388     }
1389 
1390     protected void cancelPreloadingRecents() {
1391         if (mRecents != null) {
1392             mRecents.cancelPreloadingRecents();
1393         }
1394     }
1395 
1396     protected void showRecentsNextAffiliatedTask() {
1397         if (mRecents != null) {
1398             mRecents.showNextAffiliatedTask();
1399         }
1400     }
1401 
1402     protected void showRecentsPreviousAffiliatedTask() {
1403         if (mRecents != null) {
1404             mRecents.showPrevAffiliatedTask();
1405         }
1406     }
1407 
1408     /**
1409      * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1410      */
1411     public abstract void maybeEscalateHeadsUp();
1412 
1413     /**
1414      * Save the current "public" (locked and secure) state of the lockscreen.
1415      */
1416     public void setLockscreenPublicMode(boolean publicMode) {
1417         mLockscreenPublicMode = publicMode;
1418     }
1419 
1420     public boolean isLockscreenPublicMode() {
1421         return mLockscreenPublicMode;
1422     }
1423 
1424     protected void onWorkChallengeUnlocked() {}
1425 
1426     /**
1427      * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
1428      * "public" (secure & locked) mode?
1429      */
1430     public boolean userAllowsNotificationsInPublic(int userHandle) {
1431         if (userHandle == UserHandle.USER_ALL) {
1432             return true;
1433         }
1434 
1435         if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
1436             final boolean allowed = 0 != Settings.Secure.getIntForUser(
1437                     mContext.getContentResolver(),
1438                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
1439             mUsersAllowingNotifications.append(userHandle, allowed);
1440             return allowed;
1441         }
1442 
1443         return mUsersAllowingNotifications.get(userHandle);
1444     }
1445 
1446     /**
1447      * Has the given user chosen to allow their private (full) notifications to be shown even
1448      * when the lockscreen is in "public" (secure & locked) mode?
1449      */
1450     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1451         if (userHandle == UserHandle.USER_ALL) {
1452             return true;
1453         }
1454 
1455         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1456             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
1457                     mContext.getContentResolver(),
1458                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1459             final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
1460             final boolean allowed = allowedByUser && allowedByDpm;
1461             mUsersAllowingPrivateNotifications.append(userHandle, allowed);
1462             return allowed;
1463         }
1464 
1465         return mUsersAllowingPrivateNotifications.get(userHandle);
1466     }
1467 
1468     private boolean adminAllowsUnredactedNotifications(int userHandle) {
1469         if (userHandle == UserHandle.USER_ALL) {
1470             return true;
1471         }
1472         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1473                     userHandle);
1474         return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1475     }
1476 
1477     /**
1478      * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
1479      * If so, notifications should be hidden.
1480      */
1481     @Override  // NotificationData.Environment
1482     public boolean shouldHideNotifications(int userid) {
1483         return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
1484     }
1485 
1486     /**
1487      * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
1488      * package-specific override.
1489      */
1490     @Override // NotificationDate.Environment
1491     public boolean shouldHideNotifications(String key) {
1492         return isLockscreenPublicMode()
1493                 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
1494     }
1495 
1496     /**
1497      * Returns true if we're on a secure lockscreen.
1498      */
1499     @Override  // NotificationData.Environment
1500     public boolean onSecureLockScreen() {
1501         return isLockscreenPublicMode();
1502     }
1503 
1504     public void onNotificationClear(StatusBarNotification notification) {
1505         try {
1506             mBarService.onNotificationClear(
1507                     notification.getPackageName(),
1508                     notification.getTag(),
1509                     notification.getId(),
1510                     notification.getUserId());
1511         } catch (android.os.RemoteException ex) {
1512             // oh well
1513         }
1514     }
1515 
1516     /**
1517      * Called when the notification panel layouts
1518      */
1519     public void onPanelLaidOut() {
1520         if (mState == StatusBarState.KEYGUARD) {
1521             // Since the number of notifications is determined based on the height of the view, we
1522             // need to update them.
1523             int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
1524             int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
1525             if (maxBefore != maxNotifications) {
1526                 updateRowStates();
1527             }
1528         }
1529     }
1530 
1531     protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
1532 
1533     protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
1534 
1535     protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
1536             View clicked) {}
1537 
1538     @Override
1539     public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
1540     }
1541 
1542     protected class H extends Handler {
1543         public void handleMessage(Message m) {
1544             switch (m.what) {
1545              case MSG_SHOW_RECENT_APPS:
1546                  showRecents(m.arg1 > 0, m.arg2 != 0);
1547                  break;
1548              case MSG_HIDE_RECENT_APPS:
1549                  hideRecents(m.arg1 > 0, m.arg2 > 0);
1550                  break;
1551              case MSG_TOGGLE_RECENTS_APPS:
1552                  toggleRecents();
1553                  break;
1554              case MSG_PRELOAD_RECENT_APPS:
1555                   preloadRecents();
1556                   break;
1557              case MSG_CANCEL_PRELOAD_RECENT_APPS:
1558                   cancelPreloadingRecents();
1559                   break;
1560              case MSG_SHOW_NEXT_AFFILIATED_TASK:
1561                   showRecentsNextAffiliatedTask();
1562                   break;
1563              case MSG_SHOW_PREV_AFFILIATED_TASK:
1564                   showRecentsPreviousAffiliatedTask();
1565                   break;
1566              case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
1567                   toggleKeyboardShortcuts(m.arg1);
1568                   break;
1569              case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
1570                   dismissKeyboardShortcuts();
1571                   break;
1572             }
1573         }
1574     }
1575 
1576     protected void workAroundBadLayerDrawableOpacity(View v) {
1577     }
1578 
1579     protected boolean inflateViews(Entry entry, ViewGroup parent) {
1580         PackageManager pmUser = getPackageManagerForUser(mContext,
1581                 entry.notification.getUser().getIdentifier());
1582 
1583         final StatusBarNotification sbn = entry.notification;
1584         entry.cacheContentViews(mContext, null);
1585 
1586         final RemoteViews contentView = entry.cachedContentView;
1587         final RemoteViews bigContentView = entry.cachedBigContentView;
1588         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
1589         final RemoteViews publicContentView = entry.cachedPublicContentView;
1590 
1591         if (contentView == null) {
1592             Log.v(TAG, "no contentView for: " + sbn.getNotification());
1593             return false;
1594         }
1595 
1596         if (DEBUG) {
1597             Log.v(TAG, "publicContentView: " + publicContentView);
1598         }
1599 
1600         ExpandableNotificationRow row;
1601 
1602         // Stash away previous user expansion state so we can restore it at
1603         // the end.
1604         boolean hasUserChangedExpansion = false;
1605         boolean userExpanded = false;
1606         boolean userLocked = false;
1607 
1608         if (entry.row != null) {
1609             row = entry.row;
1610             hasUserChangedExpansion = row.hasUserChangedExpansion();
1611             userExpanded = row.isUserExpanded();
1612             userLocked = row.isUserLocked();
1613             entry.reset();
1614             if (hasUserChangedExpansion) {
1615                 row.setUserExpanded(userExpanded);
1616             }
1617         } else {
1618             // create the row view
1619             LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1620                     Context.LAYOUT_INFLATER_SERVICE);
1621             row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1622                     parent, false);
1623             row.setExpansionLogger(this, entry.notification.getKey());
1624             row.setGroupManager(mGroupManager);
1625             row.setHeadsUpManager(mHeadsUpManager);
1626             row.setRemoteInputController(mRemoteInputController);
1627             row.setOnExpandClickListener(this);
1628 
1629             // Get the app name.
1630             // Note that Notification.Builder#bindHeaderAppName has similar logic
1631             // but since this field is used in the guts, it must be accurate.
1632             // Therefore we will only show the application label, or, failing that, the
1633             // package name. No substitutions.
1634             final String pkg = sbn.getPackageName();
1635             String appname = pkg;
1636             try {
1637                 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1638                         PackageManager.GET_UNINSTALLED_PACKAGES
1639                                 | PackageManager.GET_DISABLED_COMPONENTS);
1640                 if (info != null) {
1641                     appname = String.valueOf(pmUser.getApplicationLabel(info));
1642                 }
1643             } catch (NameNotFoundException e) {
1644                 // Do nothing
1645             }
1646             row.setAppName(appname);
1647         }
1648 
1649         workAroundBadLayerDrawableOpacity(row);
1650         View vetoButton = bindVetoButtonClickListener(row, sbn);
1651         vetoButton.setContentDescription(mContext.getString(
1652                 R.string.accessibility_remove_notification));
1653 
1654         // NB: the large icon is now handled entirely by the template
1655 
1656         // bind the click event to the content area
1657         NotificationContentView contentContainer = row.getPrivateLayout();
1658         NotificationContentView contentContainerPublic = row.getPublicLayout();
1659 
1660         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1661         if (ENABLE_REMOTE_INPUT) {
1662             row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1663         }
1664 
1665         mNotificationClicker.register(row, sbn);
1666 
1667         // set up the adaptive layout
1668         View contentViewLocal = null;
1669         View bigContentViewLocal = null;
1670         View headsUpContentViewLocal = null;
1671         View publicViewLocal = null;
1672         try {
1673             contentViewLocal = contentView.apply(
1674                     sbn.getPackageContext(mContext),
1675                     contentContainer,
1676                     mOnClickHandler);
1677             if (bigContentView != null) {
1678                 bigContentViewLocal = bigContentView.apply(
1679                         sbn.getPackageContext(mContext),
1680                         contentContainer,
1681                         mOnClickHandler);
1682             }
1683             if (headsUpContentView != null) {
1684                 headsUpContentViewLocal = headsUpContentView.apply(
1685                         sbn.getPackageContext(mContext),
1686                         contentContainer,
1687                         mOnClickHandler);
1688             }
1689             if (publicContentView != null) {
1690                 publicViewLocal = publicContentView.apply(
1691                         sbn.getPackageContext(mContext),
1692                         contentContainerPublic, mOnClickHandler);
1693             }
1694         }
1695         catch (RuntimeException e) {
1696             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1697             Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1698             return false;
1699         }
1700 
1701         if (contentViewLocal != null) {
1702             contentViewLocal.setIsRootNamespace(true);
1703             contentContainer.setContractedChild(contentViewLocal);
1704         }
1705         if (bigContentViewLocal != null) {
1706             bigContentViewLocal.setIsRootNamespace(true);
1707             contentContainer.setExpandedChild(bigContentViewLocal);
1708         }
1709         if (headsUpContentViewLocal != null) {
1710             headsUpContentViewLocal.setIsRootNamespace(true);
1711             contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1712         }
1713         if (publicViewLocal != null) {
1714             publicViewLocal.setIsRootNamespace(true);
1715             contentContainerPublic.setContractedChild(publicViewLocal);
1716         }
1717 
1718         // Extract target SDK version.
1719         try {
1720             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1721             entry.targetSdk = info.targetSdkVersion;
1722         } catch (NameNotFoundException ex) {
1723             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1724         }
1725         entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
1726 
1727         if (MULTIUSER_DEBUG) {
1728             TextView debug = (TextView) row.findViewById(R.id.debug_info);
1729             if (debug != null) {
1730                 debug.setVisibility(View.VISIBLE);
1731                 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1732             }
1733         }
1734         entry.row = row;
1735         entry.row.setOnActivatedListener(this);
1736         entry.row.setExpandable(bigContentViewLocal != null);
1737 
1738         applyColorsAndBackgrounds(sbn, entry);
1739 
1740         // Restore previous flags.
1741         if (hasUserChangedExpansion) {
1742             // Note: setUserExpanded() conveniently ignores calls with
1743             //       userExpanded=true if !isExpandable().
1744             row.setUserExpanded(userExpanded);
1745         }
1746         row.setUserLocked(userLocked);
1747         row.onNotificationUpdated(entry);
1748         return true;
1749     }
1750 
1751     /**
1752      * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1753      * via first-class API.
1754      *
1755      * TODO: Remove once enough apps specify remote inputs on their own.
1756      */
1757     private void processForRemoteInput(Notification n) {
1758         if (!ENABLE_REMOTE_INPUT) return;
1759 
1760         if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
1761                 (n.actions == null || n.actions.length == 0)) {
1762             Notification.Action viableAction = null;
1763             Notification.WearableExtender we = new Notification.WearableExtender(n);
1764 
1765             List<Notification.Action> actions = we.getActions();
1766             final int numActions = actions.size();
1767 
1768             for (int i = 0; i < numActions; i++) {
1769                 Notification.Action action = actions.get(i);
1770                 if (action == null) {
1771                     continue;
1772                 }
1773                 RemoteInput[] remoteInputs = action.getRemoteInputs();
1774                 if (remoteInputs == null) {
1775                     continue;
1776                 }
1777                 for (RemoteInput ri : remoteInputs) {
1778                     if (ri.getAllowFreeFormInput()) {
1779                         viableAction = action;
1780                         break;
1781                     }
1782                 }
1783                 if (viableAction != null) {
1784                     break;
1785                 }
1786             }
1787 
1788             if (viableAction != null) {
1789                 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
1790                 rebuilder.setActions(viableAction);
1791                 rebuilder.build(); // will rewrite n
1792             }
1793         }
1794     }
1795 
1796     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
1797         if (!isDeviceProvisioned()) return;
1798 
1799         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1800         final boolean afterKeyguardGone = intent.isActivity()
1801                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1802                 mCurrentUserId);
1803         dismissKeyguardThenExecute(new OnDismissAction() {
1804             public boolean onDismiss() {
1805                 new Thread() {
1806                     @Override
1807                     public void run() {
1808                         try {
1809                             if (keyguardShowing && !afterKeyguardGone) {
1810                                 ActivityManagerNative.getDefault()
1811                                         .keyguardWaitingForActivityDrawn();
1812                             }
1813 
1814                             // The intent we are sending is for the application, which
1815                             // won't have permission to immediately start an activity after
1816                             // the user switches to home.  We know it is safe to do at this
1817                             // point, so make sure new activity switches are now allowed.
1818                             ActivityManagerNative.getDefault().resumeAppSwitches();
1819                         } catch (RemoteException e) {
1820                         }
1821                         try {
1822                             intent.send(null, 0, null, null, null, null, getActivityOptions());
1823                         } catch (PendingIntent.CanceledException e) {
1824                             // the stack trace isn't very helpful here.
1825                             // Just log the exception message.
1826                             Log.w(TAG, "Sending intent failed: " + e);
1827 
1828                             // TODO: Dismiss Keyguard.
1829                         }
1830                         if (intent.isActivity()) {
1831                             mAssistManager.hideAssist();
1832                             overrideActivityPendingAppTransition(keyguardShowing
1833                                     && !afterKeyguardGone);
1834                         }
1835                     }
1836                 }.start();
1837 
1838                 // close the shade if it was open
1839                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1840                         true /* force */, true /* delayed */);
1841                 visibilityChanged(false);
1842 
1843                 return true;
1844             }
1845         }, afterKeyguardGone);
1846     }
1847 
1848     public void addPostCollapseAction(Runnable r) {
1849     }
1850 
1851     public boolean isCollapsing() {
1852         return false;
1853     }
1854 
1855     private final class NotificationClicker implements View.OnClickListener {
1856         public void onClick(final View v) {
1857             if (!(v instanceof ExpandableNotificationRow)) {
1858                 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
1859                 return;
1860             }
1861 
1862             final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1863             final StatusBarNotification sbn = row.getStatusBarNotification();
1864             if (sbn == null) {
1865                 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
1866                 return;
1867             }
1868 
1869             // Check if the notification is displaying the gear, if so slide notification back
1870             if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
1871                 row.animateTranslateNotification(0);
1872                 return;
1873             }
1874 
1875             Notification notification = sbn.getNotification();
1876             final PendingIntent intent = notification.contentIntent != null
1877                     ? notification.contentIntent
1878                     : notification.fullScreenIntent;
1879             final String notificationKey = sbn.getKey();
1880 
1881             // Mark notification for one frame.
1882             row.setJustClicked(true);
1883             DejankUtils.postAfterTraversal(new Runnable() {
1884                 @Override
1885                 public void run() {
1886                     row.setJustClicked(false);
1887                 }
1888             });
1889 
1890             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1891             final boolean afterKeyguardGone = intent.isActivity()
1892                     && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1893                             mCurrentUserId);
1894             dismissKeyguardThenExecute(new OnDismissAction() {
1895                 public boolean onDismiss() {
1896                     if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1897                         // Release the HUN notification to the shade.
1898 
1899                         if (isPanelFullyCollapsed()) {
1900                             HeadsUpManager.setIsClickedNotification(row, true);
1901                         }
1902                         //
1903                         // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1904                         // become canceled shortly by NoMan, but we can't assume that.
1905                         mHeadsUpManager.releaseImmediately(notificationKey);
1906                     }
1907                     StatusBarNotification parentToCancel = null;
1908                     if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
1909                         StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
1910                                         .getStatusBarNotification();
1911                         if (shouldAutoCancel(summarySbn)) {
1912                             parentToCancel = summarySbn;
1913                         }
1914                     }
1915                     final StatusBarNotification parentToCancelFinal = parentToCancel;
1916                     new Thread() {
1917                         @Override
1918                         public void run() {
1919                             try {
1920                                 if (keyguardShowing && !afterKeyguardGone) {
1921                                     ActivityManagerNative.getDefault()
1922                                             .keyguardWaitingForActivityDrawn();
1923                                 }
1924 
1925                                 // The intent we are sending is for the application, which
1926                                 // won't have permission to immediately start an activity after
1927                                 // the user switches to home.  We know it is safe to do at this
1928                                 // point, so make sure new activity switches are now allowed.
1929                                 ActivityManagerNative.getDefault().resumeAppSwitches();
1930                             } catch (RemoteException e) {
1931                             }
1932                             if (intent != null) {
1933                                 // If we are launching a work activity and require to launch
1934                                 // separate work challenge, we defer the activity action and cancel
1935                                 // notification until work challenge is unlocked.
1936                                 if (intent.isActivity()) {
1937                                     final int userId = intent.getCreatorUserHandle()
1938                                             .getIdentifier();
1939                                     if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
1940                                             && mKeyguardManager.isDeviceLocked(userId)) {
1941                                         if (startWorkChallengeIfNecessary(userId,
1942                                                 intent.getIntentSender(), notificationKey)) {
1943                                             // Show work challenge, do not run pendingintent and
1944                                             // remove notification
1945                                             return;
1946                                         }
1947                                     }
1948                                 }
1949                                 try {
1950                                     intent.send(null, 0, null, null, null, null,
1951                                             getActivityOptions());
1952                                 } catch (PendingIntent.CanceledException e) {
1953                                     // the stack trace isn't very helpful here.
1954                                     // Just log the exception message.
1955                                     Log.w(TAG, "Sending contentIntent failed: " + e);
1956 
1957                                     // TODO: Dismiss Keyguard.
1958                                 }
1959                                 if (intent.isActivity()) {
1960                                     mAssistManager.hideAssist();
1961                                     overrideActivityPendingAppTransition(keyguardShowing
1962                                             && !afterKeyguardGone);
1963                                 }
1964                             }
1965 
1966                             try {
1967                                 mBarService.onNotificationClick(notificationKey);
1968                             } catch (RemoteException ex) {
1969                                 // system process is dead if we're here.
1970                             }
1971                             if (parentToCancelFinal != null) {
1972                                 // We have to post it to the UI thread for synchronization
1973                                 mHandler.post(new Runnable() {
1974                                     @Override
1975                                     public void run() {
1976                                         Runnable removeRunnable = new Runnable() {
1977                                             @Override
1978                                             public void run() {
1979                                                 performRemoveNotification(parentToCancelFinal,
1980                                                         true);
1981                                             }
1982                                         };
1983                                         if (isCollapsing()) {
1984                                             // To avoid lags we're only performing the remove
1985                                             // after the shade was collapsed
1986                                             addPostCollapseAction(removeRunnable);
1987                                         } else {
1988                                             removeRunnable.run();
1989                                         }
1990                                     }
1991                                 });
1992                             }
1993                         }
1994                     }.start();
1995 
1996                     // close the shade if it was open
1997                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1998                             true /* force */, true /* delayed */);
1999                     visibilityChanged(false);
2000 
2001                     return true;
2002                 }
2003             }, afterKeyguardGone);
2004         }
2005 
2006         private boolean shouldAutoCancel(StatusBarNotification sbn) {
2007             int flags = sbn.getNotification().flags;
2008             if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
2009                 return false;
2010             }
2011             if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2012                 return false;
2013             }
2014             return true;
2015         }
2016 
2017         public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
2018             Notification notification = sbn.getNotification();
2019             if (notification.contentIntent != null || notification.fullScreenIntent != null) {
2020                 row.setOnClickListener(this);
2021             } else {
2022                 row.setOnClickListener(null);
2023             }
2024         }
2025     }
2026 
2027     public void animateCollapsePanels(int flags, boolean force) {
2028     }
2029 
2030     public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
2031     }
2032 
2033     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
2034         if (keyguardShowing) {
2035             try {
2036                 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
2037             } catch (RemoteException e) {
2038                 Log.w(TAG, "Error overriding app transition: " + e);
2039             }
2040         }
2041     }
2042 
2043     protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
2044             String notificationKey) {
2045         final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
2046                 null, userId);
2047         if (newIntent == null) {
2048             return false;
2049         }
2050         final Intent callBackIntent = new Intent(
2051                 WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
2052         callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
2053         callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
2054         callBackIntent.setPackage(mContext.getPackageName());
2055 
2056         PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
2057                 mContext,
2058                 0,
2059                 callBackIntent,
2060                 PendingIntent.FLAG_CANCEL_CURRENT |
2061                         PendingIntent.FLAG_ONE_SHOT |
2062                         PendingIntent.FLAG_IMMUTABLE);
2063         newIntent.putExtra(
2064                 Intent.EXTRA_INTENT,
2065                 callBackPendingIntent.getIntentSender());
2066         try {
2067             ActivityManagerNative.getDefault().startConfirmDeviceCredentialIntent(newIntent);
2068         } catch (RemoteException ex) {
2069             // ignore
2070         }
2071         return true;
2072     }
2073 
2074     protected Bundle getActivityOptions() {
2075         // Anything launched from the notification shade should always go into the
2076         // fullscreen stack.
2077         ActivityOptions options = ActivityOptions.makeBasic();
2078         options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
2079         return options.toBundle();
2080     }
2081 
2082     protected void visibilityChanged(boolean visible) {
2083         if (mVisible != visible) {
2084             mVisible = visible;
2085             if (!visible) {
2086                 dismissPopups();
2087             }
2088         }
2089         updateVisibleToUser();
2090     }
2091 
2092     protected void updateVisibleToUser() {
2093         boolean oldVisibleToUser = mVisibleToUser;
2094         mVisibleToUser = mVisible && mDeviceInteractive;
2095 
2096         if (oldVisibleToUser != mVisibleToUser) {
2097             handleVisibleToUserChanged(mVisibleToUser);
2098         }
2099     }
2100 
2101     /**
2102      * The LEDs are turned off when the notification panel is shown, even just a little bit.
2103      * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
2104      */
2105     protected void handleVisibleToUserChanged(boolean visibleToUser) {
2106         try {
2107             if (visibleToUser) {
2108                 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
2109                 boolean clearNotificationEffects =
2110                         !isPanelFullyCollapsed() &&
2111                         (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
2112                 int notificationLoad = mNotificationData.getActiveNotifications().size();
2113                 if (pinnedHeadsUp && isPanelFullyCollapsed())  {
2114                     notificationLoad = 1;
2115                 } else {
2116                     MetricsLogger.histogram(mContext, "note_load", notificationLoad);
2117                 }
2118                 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
2119             } else {
2120                 mBarService.onPanelHidden();
2121             }
2122         } catch (RemoteException ex) {
2123             // Won't fail unless the world has ended.
2124         }
2125     }
2126 
2127     /**
2128      * Clear Buzz/Beep/Blink.
2129      */
2130     public void clearNotificationEffects() {
2131         try {
2132             mBarService.clearNotificationEffects();
2133         } catch (RemoteException e) {
2134             // Won't fail unless the world has ended.
2135         }
2136     }
2137 
2138     public abstract boolean isPanelFullyCollapsed();
2139 
2140     /**
2141      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
2142      * about the failure.
2143      *
2144      * WARNING: this will call back into us.  Don't hold any locks.
2145      */
2146     void handleNotificationError(StatusBarNotification n, String message) {
2147         removeNotification(n.getKey(), null);
2148         try {
2149             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
2150                     n.getInitialPid(), message, n.getUserId());
2151         } catch (RemoteException ex) {
2152             // The end is nigh.
2153         }
2154     }
2155 
2156     protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
2157         NotificationData.Entry entry = mNotificationData.remove(key, ranking);
2158         if (entry == null) {
2159             Log.w(TAG, "removeNotification for unknown key: " + key);
2160             return null;
2161         }
2162         updateNotifications();
2163         return entry.notification;
2164     }
2165 
2166     protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
2167         if (DEBUG) {
2168             Log.d(TAG, "createNotificationViews(notification=" + sbn);
2169         }
2170         final StatusBarIconView iconView = createIcon(sbn);
2171         if (iconView == null) {
2172             return null;
2173         }
2174 
2175         // Construct the expanded view.
2176         NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
2177         if (!inflateViews(entry, mStackScroller)) {
2178             handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
2179             return null;
2180         }
2181         return entry;
2182     }
2183 
2184     public StatusBarIconView createIcon(StatusBarNotification sbn) {
2185         // Construct the icon.
2186         Notification n = sbn.getNotification();
2187         final StatusBarIconView iconView = new StatusBarIconView(mContext,
2188                 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
2189         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
2190 
2191         final Icon smallIcon = n.getSmallIcon();
2192         if (smallIcon == null) {
2193             handleNotificationError(sbn,
2194                     "No small icon in notification from " + sbn.getPackageName());
2195             return null;
2196         }
2197         final StatusBarIcon ic = new StatusBarIcon(
2198                 sbn.getUser(),
2199                 sbn.getPackageName(),
2200                 smallIcon,
2201                 n.iconLevel,
2202                 n.number,
2203                 StatusBarIconView.contentDescForNotification(mContext, n));
2204         if (!iconView.set(ic)) {
2205             handleNotificationError(sbn, "Couldn't create icon: " + ic);
2206             return null;
2207         }
2208         return iconView;
2209     }
2210 
2211     protected void addNotificationViews(Entry entry, RankingMap ranking) {
2212         if (entry == null) {
2213             return;
2214         }
2215         // Add the expanded view and icon.
2216         mNotificationData.add(entry, ranking);
2217         updateNotifications();
2218     }
2219 
2220     /**
2221      * @param recompute wheter the number should be recomputed
2222      * @return The number of notifications we show on Keyguard.
2223      */
2224     protected abstract int getMaxKeyguardNotifications(boolean recompute);
2225 
2226     /**
2227      * Updates expanded, dimmed and locked states of notification rows.
2228      */
2229     protected void updateRowStates() {
2230         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
2231 
2232         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
2233         final int N = activeNotifications.size();
2234 
2235         int visibleNotifications = 0;
2236         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
2237         int maxNotifications = 0;
2238         if (onKeyguard) {
2239             maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
2240         }
2241         for (int i = 0; i < N; i++) {
2242             NotificationData.Entry entry = activeNotifications.get(i);
2243             boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
2244             if (onKeyguard) {
2245                 entry.row.setOnKeyguard(true);
2246             } else {
2247                 entry.row.setOnKeyguard(false);
2248                 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
2249             }
2250             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
2251                     entry.notification) && !entry.row.isRemoved();
2252             boolean childWithVisibleSummary = childNotification
2253                     && mGroupManager.getGroupSummary(entry.notification).getVisibility()
2254                     == View.VISIBLE;
2255             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
2256             if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
2257                     (onKeyguard && !childWithVisibleSummary
2258                             && (visibleNotifications >= maxNotifications || !showOnKeyguard))) {
2259                 entry.row.setVisibility(View.GONE);
2260                 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
2261                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
2262                 }
2263             } else {
2264                 boolean wasGone = entry.row.getVisibility() == View.GONE;
2265                 entry.row.setVisibility(View.VISIBLE);
2266                 if (!childNotification && !entry.row.isRemoved()) {
2267                     if (wasGone) {
2268                         // notify the scroller of a child addition
2269                         mStackScroller.generateAddAnimation(entry.row,
2270                                 !showOnKeyguard /* fromMoreCard */);
2271                     }
2272                     visibleNotifications++;
2273                 }
2274             }
2275         }
2276 
2277         mStackScroller.updateOverflowContainerVisibility(onKeyguard
2278                 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
2279 
2280         mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
2281         mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
2282         mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
2283                 mStackScroller.getChildCount() - 3);
2284     }
2285 
2286     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
2287         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
2288     }
2289 
2290     protected void setZenMode(int mode) {
2291         if (!isDeviceProvisioned()) return;
2292         mZenMode = mode;
2293         updateNotifications();
2294     }
2295 
2296     // extended in PhoneStatusBar
2297     protected void setShowLockscreenNotifications(boolean show) {
2298         mShowLockscreenNotifications = show;
2299     }
2300 
2301     protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
2302         mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
2303     }
2304 
2305     private void updateLockscreenNotificationSetting() {
2306         final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2307                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
2308                 1,
2309                 mCurrentUserId) != 0;
2310         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
2311                 null /* admin */, mCurrentUserId);
2312         final boolean allowedByDpm = (dpmFlags
2313                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
2314 
2315         setShowLockscreenNotifications(show && allowedByDpm);
2316 
2317         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
2318             final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2319                     Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
2320                     0,
2321                     mCurrentUserId) != 0;
2322             final boolean remoteInputDpm =
2323                     (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
2324 
2325             setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
2326         } else {
2327             setLockScreenAllowRemoteInput(false);
2328         }
2329     }
2330 
2331     protected abstract void setAreThereNotifications();
2332     protected abstract void updateNotifications();
2333     public abstract boolean shouldDisableNavbarGestures();
2334 
2335     public abstract void addNotification(StatusBarNotification notification,
2336             RankingMap ranking, Entry oldEntry);
2337     protected abstract void updateNotificationRanking(RankingMap ranking);
2338     public abstract void removeNotification(String key, RankingMap ranking);
2339 
2340     public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
2341         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
2342 
2343         final String key = notification.getKey();
2344         Entry entry = mNotificationData.get(key);
2345         if (entry == null) {
2346             return;
2347         } else {
2348             mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2349             mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
2350         }
2351 
2352         Notification n = notification.getNotification();
2353         mNotificationData.updateRanking(ranking);
2354 
2355         boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
2356         boolean shouldPeek = shouldPeek(entry, notification);
2357         boolean alertAgain = alertAgain(entry, n);
2358         if (DEBUG) {
2359             Log.d(TAG, "applyInPlace=" + applyInPlace
2360                     + " shouldPeek=" + shouldPeek
2361                     + " alertAgain=" + alertAgain);
2362         }
2363 
2364         final StatusBarNotification oldNotification = entry.notification;
2365         entry.notification = notification;
2366         mGroupManager.onEntryUpdated(entry, oldNotification);
2367 
2368         boolean updateSuccessful = false;
2369         if (applyInPlace) {
2370             if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
2371             try {
2372                 if (entry.icon != null) {
2373                     // Update the icon
2374                     final StatusBarIcon ic = new StatusBarIcon(
2375                             notification.getUser(),
2376                             notification.getPackageName(),
2377                             n.getSmallIcon(),
2378                             n.iconLevel,
2379                             n.number,
2380                             StatusBarIconView.contentDescForNotification(mContext, n));
2381                     entry.icon.setNotification(n);
2382                     if (!entry.icon.set(ic)) {
2383                         handleNotificationError(notification, "Couldn't update icon: " + ic);
2384                         return;
2385                     }
2386                 }
2387                 updateNotificationViews(entry, notification);
2388                 updateSuccessful = true;
2389             }
2390             catch (RuntimeException e) {
2391                 // It failed to apply cleanly.
2392                 Log.w(TAG, "Couldn't reapply views for package " +
2393                         notification.getPackageName(), e);
2394             }
2395         }
2396         if (!updateSuccessful) {
2397             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
2398             final StatusBarIcon ic = new StatusBarIcon(
2399                     notification.getUser(),
2400                     notification.getPackageName(),
2401                     n.getSmallIcon(),
2402                     n.iconLevel,
2403                     n.number,
2404                     StatusBarIconView.contentDescForNotification(mContext, n));
2405             entry.icon.setNotification(n);
2406             entry.icon.set(ic);
2407             inflateViews(entry, mStackScroller);
2408         }
2409         updateHeadsUp(key, entry, shouldPeek, alertAgain);
2410         updateNotifications();
2411 
2412         // Update the veto button accordingly (and as a result, whether this row is
2413         // swipe-dismissable)
2414         bindVetoButtonClickListener(entry.row, notification);
2415 
2416         if (!notification.isClearable()) {
2417             // The user may have performed a dismiss action on the notification, since it's
2418             // not clearable we should snap it back.
2419             mStackScroller.snapViewIfNeeded(entry.row);
2420         }
2421 
2422         if (DEBUG) {
2423             // Is this for you?
2424             boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
2425             Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
2426         }
2427 
2428         setAreThereNotifications();
2429     }
2430 
2431     protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2432             boolean alertAgain);
2433 
2434     private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
2435         final RemoteViews contentView = entry.cachedContentView;
2436         final RemoteViews bigContentView = entry.cachedBigContentView;
2437         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
2438         final RemoteViews publicContentView = entry.cachedPublicContentView;
2439 
2440         // Reapply the RemoteViews
2441         contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
2442         if (bigContentView != null && entry.getExpandedContentView() != null) {
2443             bigContentView.reapply(sbn.getPackageContext(mContext),
2444                     entry.getExpandedContentView(),
2445                     mOnClickHandler);
2446         }
2447         View headsUpChild = entry.getHeadsUpContentView();
2448         if (headsUpContentView != null && headsUpChild != null) {
2449             headsUpContentView.reapply(sbn.getPackageContext(mContext),
2450                     headsUpChild, mOnClickHandler);
2451         }
2452         if (publicContentView != null && entry.getPublicContentView() != null) {
2453             publicContentView.reapply(sbn.getPackageContext(mContext),
2454                     entry.getPublicContentView(), mOnClickHandler);
2455         }
2456         // update the contentIntent
2457         mNotificationClicker.register(entry.row, sbn);
2458 
2459         entry.row.onNotificationUpdated(entry);
2460         entry.row.resetHeight();
2461     }
2462 
2463     protected void updatePublicContentView(Entry entry,
2464             StatusBarNotification sbn) {
2465         final RemoteViews publicContentView = entry.cachedPublicContentView;
2466         View inflatedView = entry.getPublicContentView();
2467         if (entry.autoRedacted && publicContentView != null && inflatedView != null) {
2468             final boolean disabledByPolicy =
2469                     !adminAllowsUnredactedNotifications(entry.notification.getUserId());
2470             String notificationHiddenText = mContext.getString(disabledByPolicy
2471                     ? com.android.internal.R.string.notification_hidden_by_policy_text
2472                     : com.android.internal.R.string.notification_hidden_text);
2473             TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
2474             if (titleView != null
2475                     && !titleView.getText().toString().equals(notificationHiddenText)) {
2476                 publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
2477                 publicContentView.reapply(sbn.getPackageContext(mContext),
2478                         inflatedView, mOnClickHandler);
2479                 entry.row.onNotificationUpdated(entry);
2480             }
2481         }
2482     }
2483 
2484     protected void notifyHeadsUpScreenOff() {
2485         maybeEscalateHeadsUp();
2486     }
2487 
2488     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2489         return oldEntry == null || !oldEntry.hasInterrupted()
2490                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2491     }
2492 
2493     protected boolean shouldPeek(Entry entry) {
2494         return shouldPeek(entry, entry.notification);
2495     }
2496 
2497     protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
2498         if (!mUseHeadsUp || isDeviceInVrMode()) {
2499             return false;
2500         }
2501 
2502         if (mNotificationData.shouldFilterOut(sbn)) {
2503             if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
2504             return false;
2505         }
2506 
2507         boolean inUse = mPowerManager.isScreenOn()
2508                 && (!mStatusBarKeyguardViewManager.isShowing()
2509                 || mStatusBarKeyguardViewManager.isOccluded())
2510                 && !mStatusBarKeyguardViewManager.isInputRestricted();
2511         try {
2512             inUse = inUse && !mDreamManager.isDreaming();
2513         } catch (RemoteException e) {
2514             Log.d(TAG, "failed to query dream manager", e);
2515         }
2516 
2517         if (!inUse) {
2518             if (DEBUG) {
2519                 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
2520             }
2521             return false;
2522         }
2523 
2524         if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
2525             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
2526             return false;
2527         }
2528 
2529         if (entry.hasJustLaunchedFullScreenIntent()) {
2530             if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
2531             return false;
2532         }
2533 
2534         if (isSnoozedPackage(sbn)) {
2535             if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
2536             return false;
2537         }
2538 
2539         if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
2540             if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
2541             return false;
2542         }
2543 
2544         if (sbn.getNotification().fullScreenIntent != null) {
2545             if (mAccessibilityManager.isTouchExplorationEnabled()) {
2546                 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
2547                 return false;
2548             } else {
2549                 return true;
2550             }
2551         }
2552 
2553         return true;
2554     }
2555 
2556     protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2557 
2558     public void setInteracting(int barWindow, boolean interacting) {
2559         // hook for subclasses
2560     }
2561 
2562     public void setBouncerShowing(boolean bouncerShowing) {
2563         mBouncerShowing = bouncerShowing;
2564     }
2565 
2566     /**
2567      * @return Whether the security bouncer from Keyguard is showing.
2568      */
2569     public boolean isBouncerShowing() {
2570         return mBouncerShowing;
2571     }
2572 
2573     public void destroy() {
2574         mContext.unregisterReceiver(mBroadcastReceiver);
2575         try {
2576             mNotificationListener.unregisterAsSystemService();
2577         } catch (RemoteException e) {
2578             // Ignore.
2579         }
2580     }
2581 
2582     /**
2583      * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2584      *         return PackageManager for mContext
2585      */
2586     public static PackageManager getPackageManagerForUser(Context context, int userId) {
2587         Context contextForUser = context;
2588         // UserHandle defines special userId as negative values, e.g. USER_ALL
2589         if (userId >= 0) {
2590             try {
2591                 // Create a context for the correct user so if a package isn't installed
2592                 // for user 0 we can still load information about the package.
2593                 contextForUser =
2594                         context.createPackageContextAsUser(context.getPackageName(),
2595                         Context.CONTEXT_RESTRICTED,
2596                         new UserHandle(userId));
2597             } catch (NameNotFoundException e) {
2598                 // Shouldn't fail to find the package name for system ui.
2599             }
2600         }
2601         return contextForUser.getPackageManager();
2602     }
2603 
2604     @Override
2605     public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2606         try {
2607             mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2608         } catch (RemoteException e) {
2609             // Ignore.
2610         }
2611     }
2612 
2613     public boolean isKeyguardSecure() {
2614         if (mStatusBarKeyguardViewManager == null) {
2615             // startKeyguard() hasn't been called yet, so we don't know.
2616             // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2617             // value onVisibilityChanged().
2618             Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2619                     new Throwable());
2620             return false;
2621         }
2622         return mStatusBarKeyguardViewManager.isSecure();
2623     }
2624 
2625     @Override
2626     public void showAssistDisclosure() {
2627         if (mAssistManager != null) {
2628             mAssistManager.showDisclosure();
2629         }
2630     }
2631 
2632     @Override
2633     public void startAssist(Bundle args) {
2634         if (mAssistManager != null) {
2635             mAssistManager.startAssist(args);
2636         }
2637     }
2638 }
2639