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