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