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.animation.TimeInterpolator;
22 import android.app.ActivityManager;
23 import android.app.ActivityManagerNative;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.TaskStackBuilder;
28 import android.app.admin.DevicePolicyManager;
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.pm.ApplicationInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManager.NameNotFoundException;
37 import android.content.pm.ResolveInfo;
38 import android.content.pm.UserInfo;
39 import android.content.res.Configuration;
40 import android.content.res.Resources;
41 import android.database.ContentObserver;
42 import android.graphics.PorterDuff;
43 import android.graphics.drawable.Drawable;
44 import android.graphics.drawable.Icon;
45 import android.os.AsyncTask;
46 import android.os.Build;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.IBinder;
50 import android.os.Message;
51 import android.os.PowerManager;
52 import android.os.RemoteException;
53 import android.os.ServiceManager;
54 import android.os.SystemProperties;
55 import android.os.UserHandle;
56 import android.os.UserManager;
57 import android.provider.Settings;
58 import android.service.dreams.DreamService;
59 import android.service.dreams.IDreamManager;
60 import android.service.notification.NotificationListenerService;
61 import android.service.notification.NotificationListenerService.RankingMap;
62 import android.service.notification.StatusBarNotification;
63 import android.text.TextUtils;
64 import android.util.Log;
65 import android.util.Slog;
66 import android.util.SparseArray;
67 import android.util.SparseBooleanArray;
68 import android.view.Display;
69 import android.view.IWindowManager;
70 import android.view.LayoutInflater;
71 import android.view.MotionEvent;
72 import android.view.View;
73 import android.view.ViewAnimationUtils;
74 import android.view.ViewGroup;
75 import android.view.ViewParent;
76 import android.view.WindowManager;
77 import android.view.WindowManagerGlobal;
78 import android.view.accessibility.AccessibilityManager;
79 import android.view.animation.AnimationUtils;
80 import android.widget.DateTimeView;
81 import android.widget.ImageView;
82 import android.widget.RemoteViews;
83 import android.widget.TextView;
84 import android.widget.Toast;
85 
86 import com.android.internal.logging.MetricsLogger;
87 import com.android.internal.statusbar.IStatusBarService;
88 import com.android.internal.statusbar.StatusBarIcon;
89 import com.android.internal.statusbar.StatusBarIconList;
90 import com.android.internal.util.NotificationColorUtil;
91 import com.android.internal.widget.LockPatternUtils;
92 import com.android.keyguard.KeyguardUpdateMonitor;
93 import com.android.systemui.R;
94 import com.android.systemui.RecentsComponent;
95 import com.android.systemui.SwipeHelper;
96 import com.android.systemui.SystemUI;
97 import com.android.systemui.assist.AssistManager;
98 import com.android.systemui.recents.Recents;
99 import com.android.systemui.statusbar.NotificationData.Entry;
100 import com.android.systemui.statusbar.phone.NavigationBarView;
101 import com.android.systemui.statusbar.phone.NotificationGroupManager;
102 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
103 import com.android.systemui.statusbar.policy.HeadsUpManager;
104 import com.android.systemui.statusbar.policy.PreviewInflater;
105 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
106 
107 import java.util.ArrayList;
108 import java.util.List;
109 import java.util.Locale;
110 
111 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
112 
113 public abstract class BaseStatusBar extends SystemUI implements
114         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
115         RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
116         NotificationData.Environment {
117     public static final String TAG = "StatusBar";
118     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
119     public static final boolean MULTIUSER_DEBUG = false;
120 
121     // STOPSHIP disable once we resolve b/18102199
122     private static final boolean NOTIFICATION_CLICK_DEBUG = true;
123 
124     public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
125                     && SystemProperties.getBoolean("debug.child_notifs", false);
126 
127     protected static final int MSG_SHOW_RECENT_APPS = 1019;
128     protected static final int MSG_HIDE_RECENT_APPS = 1020;
129     protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
130     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
131     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
132     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
133     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
134 
135     protected static final boolean ENABLE_HEADS_UP = true;
136     // scores above this threshold should be displayed in heads up mode.
137     protected static final int INTERRUPTION_THRESHOLD = 10;
138     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
139 
140     // Should match the value in PhoneWindowManager
141     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
142 
143     private static final String BANNER_ACTION_CANCEL =
144             "com.android.systemui.statusbar.banner_action_cancel";
145     private static final String BANNER_ACTION_SETUP =
146             "com.android.systemui.statusbar.banner_action_setup";
147 
148     protected CommandQueue mCommandQueue;
149     protected IStatusBarService mBarService;
150     protected H mHandler = createHandler();
151 
152     // all notifications
153     protected NotificationData mNotificationData;
154     protected NotificationStackScrollLayout mStackScroller;
155 
156     protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
157 
158     // for heads up notifications
159     protected HeadsUpManager mHeadsUpManager;
160 
161     protected int mCurrentUserId = 0;
162     final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
163 
164     protected int mLayoutDirection = -1; // invalid
165     protected AccessibilityManager mAccessibilityManager;
166 
167     // on-screen navigation buttons
168     protected NavigationBarView mNavigationBarView = null;
169 
170     protected Boolean mScreenOn;
171 
172     // The second field is a bit different from the first one because it only listens to screen on/
173     // screen of events from Keyguard. We need this so we don't have a race condition with the
174     // broadcast. In the future, we should remove the first field altogether and rename the second
175     // field.
176     protected boolean mScreenOnFromKeyguard;
177 
178     protected boolean mVisible;
179 
180     // mScreenOnFromKeyguard && mVisible.
181     private boolean mVisibleToUser;
182 
183     private Locale mLocale;
184     private float mFontScale;
185 
186     protected boolean mUseHeadsUp = false;
187     protected boolean mHeadsUpTicker = false;
188     protected boolean mDisableNotificationAlerts = false;
189 
190     protected DevicePolicyManager mDevicePolicyManager;
191     protected IDreamManager mDreamManager;
192     PowerManager mPowerManager;
193     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
194     protected int mRowMinHeight;
195     protected int mRowMaxHeight;
196 
197     // public mode, private notifications, etc
198     private boolean mLockscreenPublicMode = false;
199     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
200     private NotificationColorUtil mNotificationColorUtil;
201 
202     private UserManager mUserManager;
203 
204     // UI-specific methods
205 
206     /**
207      * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
208      * and add them to the window manager.
209      */
createAndAddWindows()210     protected abstract void createAndAddWindows();
211 
212     protected WindowManager mWindowManager;
213     protected IWindowManager mWindowManagerService;
214 
refreshLayout(int layoutDirection)215     protected abstract void refreshLayout(int layoutDirection);
216 
217     protected Display mDisplay;
218 
219     private boolean mDeviceProvisioned = false;
220 
221     private RecentsComponent mRecents;
222 
223     protected int mZenMode;
224 
225     // which notification is currently being longpress-examined by the user
226     private NotificationGuts mNotificationGutsExposed;
227 
228     private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
229 
230     /**
231      * The {@link StatusBarState} of the status bar.
232      */
233     protected int mState;
234     protected boolean mBouncerShowing;
235     protected boolean mShowLockscreenNotifications;
236 
237     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
238     protected DismissView mDismissView;
239     protected EmptyShadeView mEmptyShadeView;
240 
241     private NotificationClicker mNotificationClicker = new NotificationClicker();
242 
243     protected AssistManager mAssistManager;
244 
245     @Override  // NotificationData.Environment
isDeviceProvisioned()246     public boolean isDeviceProvisioned() {
247         return mDeviceProvisioned;
248     }
249 
250     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
251         @Override
252         public void onChange(boolean selfChange) {
253             final boolean provisioned = 0 != Settings.Global.getInt(
254                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
255             if (provisioned != mDeviceProvisioned) {
256                 mDeviceProvisioned = provisioned;
257                 updateNotifications();
258             }
259             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
260                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
261             setZenMode(mode);
262 
263             updateLockscreenNotificationSetting();
264         }
265     };
266 
267     private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
268         @Override
269         public void onChange(boolean selfChange) {
270             // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
271             // so we just dump our cache ...
272             mUsersAllowingPrivateNotifications.clear();
273             // ... and refresh all the notifications
274             updateNotifications();
275         }
276     };
277 
278     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
279         @Override
280         public boolean onClickHandler(
281                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
282             if (DEBUG) {
283                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
284             }
285             logActionClick(view);
286             // The intent we are sending is for the application, which
287             // won't have permission to immediately start an activity after
288             // the user switches to home.  We know it is safe to do at this
289             // point, so make sure new activity switches are now allowed.
290             try {
291                 ActivityManagerNative.getDefault().resumeAppSwitches();
292             } catch (RemoteException e) {
293             }
294             final boolean isActivity = pendingIntent.isActivity();
295             if (isActivity) {
296                 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
297                 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
298                         mContext, pendingIntent.getIntent(), mCurrentUserId);
299                 dismissKeyguardThenExecute(new OnDismissAction() {
300                     @Override
301                     public boolean onDismiss() {
302                         if (keyguardShowing && !afterKeyguardGone) {
303                             try {
304                                 ActivityManagerNative.getDefault()
305                                         .keyguardWaitingForActivityDrawn();
306                                 ActivityManagerNative.getDefault().resumeAppSwitches();
307                             } catch (RemoteException e) {
308                             }
309                         }
310 
311                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
312                         overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
313 
314                         // close the shade if it was open
315                         if (handled) {
316                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
317                                     true /* force */);
318                             visibilityChanged(false);
319                             mAssistManager.hideAssist();
320                         }
321 
322                         // Wait for activity start.
323                         return handled;
324                     }
325                 }, afterKeyguardGone);
326                 return true;
327             } else {
328                 return super.onClickHandler(view, pendingIntent, fillInIntent);
329             }
330         }
331 
332         private void logActionClick(View view) {
333             ViewParent parent = view.getParent();
334             String key = getNotificationKeyForParent(parent);
335             if (key == null) {
336                 Log.w(TAG, "Couldn't determine notification for click.");
337                 return;
338             }
339             int index = -1;
340             // If this is a default template, determine the index of the button.
341             if (view.getId() == com.android.internal.R.id.action0 &&
342                     parent != null && parent instanceof ViewGroup) {
343                 ViewGroup actionGroup = (ViewGroup) parent;
344                 index = actionGroup.indexOfChild(view);
345             }
346             if (NOTIFICATION_CLICK_DEBUG) {
347                 Log.d(TAG, "Clicked on button " + index + " for " + key);
348             }
349             try {
350                 mBarService.onNotificationActionClick(key, index);
351             } catch (RemoteException e) {
352                 // Ignore
353             }
354         }
355 
356         private String getNotificationKeyForParent(ViewParent parent) {
357             while (parent != null) {
358                 if (parent instanceof ExpandableNotificationRow) {
359                     return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
360                 }
361                 parent = parent.getParent();
362             }
363             return null;
364         }
365 
366         private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
367                 Intent fillInIntent) {
368             return super.onClickHandler(view, pendingIntent, fillInIntent);
369         }
370     };
371 
372     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
373         @Override
374         public void onReceive(Context context, Intent intent) {
375             String action = intent.getAction();
376             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
377                 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
378                 updateCurrentProfilesCache();
379                 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
380 
381                 updateLockscreenNotificationSetting();
382 
383                 userSwitched(mCurrentUserId);
384             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
385                 updateCurrentProfilesCache();
386             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
387                 List<ActivityManager.RecentTaskInfo> recentTask = null;
388                 try {
389                     recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
390                             ActivityManager.RECENT_WITH_EXCLUDED
391                             | ActivityManager.RECENT_INCLUDE_PROFILES,
392                             mCurrentUserId);
393                 } catch (RemoteException e) {
394                     // Abandon hope activity manager not running.
395                 }
396                 if (recentTask != null && recentTask.size() > 0) {
397                     UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
398                     if (user != null && user.isManagedProfile()) {
399                         Toast toast = Toast.makeText(mContext,
400                                 R.string.managed_profile_foreground_toast,
401                                 Toast.LENGTH_SHORT);
402                         TextView text = (TextView) toast.getView().findViewById(
403                                 android.R.id.message);
404                         text.setCompoundDrawablesRelativeWithIntrinsicBounds(
405                                 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
406                         int paddingPx = mContext.getResources().getDimensionPixelSize(
407                                 R.dimen.managed_profile_toast_padding);
408                         text.setCompoundDrawablePadding(paddingPx);
409                         toast.show();
410                     }
411                 }
412             } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
413                 NotificationManager noMan = (NotificationManager)
414                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
415                 noMan.cancel(R.id.notification_hidden);
416 
417                 Settings.Secure.putInt(mContext.getContentResolver(),
418                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
419                 if (BANNER_ACTION_SETUP.equals(action)) {
420                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
421                             true /* force */);
422                     mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
423                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
424 
425                     );
426                 }
427             }
428         }
429     };
430 
431     private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
432         @Override
433         public void onReceive(Context context, Intent intent) {
434             String action = intent.getAction();
435             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
436                     isCurrentProfile(getSendingUserId())) {
437                 mUsersAllowingPrivateNotifications.clear();
438                 updateLockscreenNotificationSetting();
439                 updateNotifications();
440             }
441         }
442     };
443 
444     private final NotificationListenerService mNotificationListener =
445             new NotificationListenerService() {
446         @Override
447         public void onListenerConnected() {
448             if (DEBUG) Log.d(TAG, "onListenerConnected");
449             final StatusBarNotification[] notifications = getActiveNotifications();
450             final RankingMap currentRanking = getCurrentRanking();
451             mHandler.post(new Runnable() {
452                 @Override
453                 public void run() {
454                     for (StatusBarNotification sbn : notifications) {
455                         addNotification(sbn, currentRanking, null /* oldEntry */);
456                     }
457                 }
458             });
459         }
460 
461         @Override
462         public void onNotificationPosted(final StatusBarNotification sbn,
463                 final RankingMap rankingMap) {
464             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
465             if (sbn != null) {
466                 mHandler.post(new Runnable() {
467                     @Override
468                     public void run() {
469 
470                         String key = sbn.getKey();
471                         boolean isUpdate = mNotificationData.get(key) != null;
472 
473                         // In case we don't allow child notifications, we ignore children of
474                         // notifications that have a summary, since we're not going to show them
475                         // anyway. This is true also when the summary is canceled,
476                         // because children are automatically canceled by NoMan in that case.
477                         if (!ENABLE_CHILD_NOTIFICATIONS
478                             && mGroupManager.isChildInGroupWithSummary(sbn)) {
479                             if (DEBUG) {
480                                 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
481                             }
482 
483                             // Remove existing notification to avoid stale data.
484                             if (isUpdate) {
485                                 removeNotification(key, rankingMap);
486                             } else {
487                                 mNotificationData.updateRanking(rankingMap);
488                             }
489                             return;
490                         }
491                         if (isUpdate) {
492                             updateNotification(sbn, rankingMap);
493                         } else {
494                             addNotification(sbn, rankingMap, null /* oldEntry */);
495                         }
496                     }
497                 });
498             }
499         }
500 
501         @Override
502         public void onNotificationRemoved(StatusBarNotification sbn,
503                 final RankingMap rankingMap) {
504             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
505             if (sbn != null) {
506                 final String key = sbn.getKey();
507                 mHandler.post(new Runnable() {
508                     @Override
509                     public void run() {
510                         removeNotification(key, rankingMap);
511                     }
512                 });
513             }
514         }
515 
516         @Override
517         public void onNotificationRankingUpdate(final RankingMap rankingMap) {
518             if (DEBUG) Log.d(TAG, "onRankingUpdate");
519             if (rankingMap != null) {
520             mHandler.post(new Runnable() {
521                 @Override
522                 public void run() {
523                     updateNotificationRanking(rankingMap);
524                 }
525             });
526         }                            }
527 
528     };
529 
updateCurrentProfilesCache()530     private void updateCurrentProfilesCache() {
531         synchronized (mCurrentProfiles) {
532             mCurrentProfiles.clear();
533             if (mUserManager != null) {
534                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
535                     mCurrentProfiles.put(user.id, user);
536                 }
537             }
538         }
539     }
540 
start()541     public void start() {
542         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
543         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
544         mDisplay = mWindowManager.getDefaultDisplay();
545         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
546                 Context.DEVICE_POLICY_SERVICE);
547 
548         mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
549 
550         mNotificationData = new NotificationData(this);
551 
552         mAccessibilityManager = (AccessibilityManager)
553                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
554 
555         mDreamManager = IDreamManager.Stub.asInterface(
556                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
557         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
558 
559         mContext.getContentResolver().registerContentObserver(
560                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
561                 mSettingsObserver);
562         mContext.getContentResolver().registerContentObserver(
563                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
564                 mSettingsObserver);
565         mContext.getContentResolver().registerContentObserver(
566                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
567                 mSettingsObserver,
568                 UserHandle.USER_ALL);
569 
570         mContext.getContentResolver().registerContentObserver(
571                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
572                 true,
573                 mLockscreenSettingsObserver,
574                 UserHandle.USER_ALL);
575 
576         mBarService = IStatusBarService.Stub.asInterface(
577                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
578 
579         mRecents = getComponent(Recents.class);
580         mRecents.setCallback(this);
581 
582         final Configuration currentConfig = mContext.getResources().getConfiguration();
583         mLocale = currentConfig.locale;
584         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
585         mFontScale = currentConfig.fontScale;
586 
587         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
588 
589         mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
590                 android.R.interpolator.linear_out_slow_in);
591         mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
592                 android.R.interpolator.fast_out_linear_in);
593 
594         // Connect in to the status bar manager service
595         StatusBarIconList iconList = new StatusBarIconList();
596         mCommandQueue = new CommandQueue(this, iconList);
597 
598         int[] switches = new int[8];
599         ArrayList<IBinder> binders = new ArrayList<IBinder>();
600         try {
601             mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
602         } catch (RemoteException ex) {
603             // If the system process isn't there we're doomed anyway.
604         }
605 
606         createAndAddWindows();
607 
608         mSettingsObserver.onChange(false); // set up
609         disable(switches[0], switches[6], false /* animate */);
610         setSystemUiVisibility(switches[1], 0xffffffff);
611         topAppWindowChanged(switches[2] != 0);
612         // StatusBarManagerService has a back up of IME token and it's restored here.
613         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
614 
615         // Set up the initial icon state
616         int N = iconList.size();
617         int viewIndex = 0;
618         for (int i=0; i<N; i++) {
619             StatusBarIcon icon = iconList.getIcon(i);
620             if (icon != null) {
621                 addIcon(iconList.getSlot(i), i, viewIndex, icon);
622                 viewIndex++;
623             }
624         }
625 
626         // Set up the initial notification state.
627         try {
628             mNotificationListener.registerAsSystemService(mContext,
629                     new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
630                     UserHandle.USER_ALL);
631         } catch (RemoteException e) {
632             Log.e(TAG, "Unable to register notification listener", e);
633         }
634 
635 
636         if (DEBUG) {
637             Log.d(TAG, String.format(
638                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
639                    iconList.size(),
640                    switches[0],
641                    switches[1],
642                    switches[2],
643                    switches[3]
644                    ));
645         }
646 
647         mCurrentUserId = ActivityManager.getCurrentUser();
648         setHeadsUpUser(mCurrentUserId);
649 
650         IntentFilter filter = new IntentFilter();
651         filter.addAction(Intent.ACTION_USER_SWITCHED);
652         filter.addAction(Intent.ACTION_USER_ADDED);
653         filter.addAction(Intent.ACTION_USER_PRESENT);
654         filter.addAction(BANNER_ACTION_CANCEL);
655         filter.addAction(BANNER_ACTION_SETUP);
656         mContext.registerReceiver(mBroadcastReceiver, filter);
657 
658         IntentFilter allUsersFilter = new IntentFilter();
659         allUsersFilter.addAction(
660                 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
661         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
662                 null, null);
663         updateCurrentProfilesCache();
664     }
665 
notifyUserAboutHiddenNotifications()666     protected void notifyUserAboutHiddenNotifications() {
667         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
668                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
669             Log.d(TAG, "user hasn't seen notification about hidden notifications");
670             final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
671             if (!lockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
672                 Log.d(TAG, "insecure lockscreen, skipping notification");
673                 Settings.Secure.putInt(mContext.getContentResolver(),
674                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
675                 return;
676             }
677             Log.d(TAG, "disabling lockecreen notifications and alerting the user");
678             // disable lockscreen notifications until user acts on the banner.
679             Settings.Secure.putInt(mContext.getContentResolver(),
680                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
681             Settings.Secure.putInt(mContext.getContentResolver(),
682                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
683 
684             final String packageName = mContext.getPackageName();
685             PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
686                     new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
687                     PendingIntent.FLAG_CANCEL_CURRENT);
688             PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
689                     new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
690                     PendingIntent.FLAG_CANCEL_CURRENT);
691 
692             final Resources res = mContext.getResources();
693             final int colorRes = com.android.internal.R.color.system_notification_accent_color;
694             Notification.Builder note = new Notification.Builder(mContext)
695                     .setSmallIcon(R.drawable.ic_android)
696                     .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
697                     .setContentText(mContext.getString(R.string.hidden_notifications_text))
698                     .setPriority(Notification.PRIORITY_HIGH)
699                     .setOngoing(true)
700                     .setColor(mContext.getColor(colorRes))
701                     .setContentIntent(setupIntent)
702                     .addAction(R.drawable.ic_close,
703                             mContext.getString(R.string.hidden_notifications_cancel),
704                             cancelIntent)
705                     .addAction(R.drawable.ic_settings,
706                             mContext.getString(R.string.hidden_notifications_setup),
707                             setupIntent);
708 
709             NotificationManager noMan =
710                     (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
711             noMan.notify(R.id.notification_hidden, note.build());
712         }
713     }
714 
userSwitched(int newUserId)715     public void userSwitched(int newUserId) {
716         setHeadsUpUser(newUserId);
717     }
718 
setHeadsUpUser(int newUserId)719     protected abstract void setHeadsUpUser(int newUserId);
720 
721     @Override  // NotificationData.Environment
isNotificationForCurrentProfiles(StatusBarNotification n)722     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
723         final int thisUserId = mCurrentUserId;
724         final int notificationUserId = n.getUserId();
725         if (DEBUG && MULTIUSER_DEBUG) {
726             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
727                     n, thisUserId, notificationUserId));
728         }
729         return isCurrentProfile(notificationUserId);
730     }
731 
setNotificationShown(StatusBarNotification n)732     protected void setNotificationShown(StatusBarNotification n) {
733         setNotificationsShown(new String[]{n.getKey()});
734     }
735 
setNotificationsShown(String[] keys)736     protected void setNotificationsShown(String[] keys) {
737         try {
738             mNotificationListener.setNotificationsShown(keys);
739         } catch (RuntimeException e) {
740             Log.d(TAG, "failed setNotificationsShown: ", e);
741         }
742     }
743 
isCurrentProfile(int userId)744     protected boolean isCurrentProfile(int userId) {
745         synchronized (mCurrentProfiles) {
746             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
747         }
748     }
749 
750     @Override
getCurrentMediaNotificationKey()751     public String getCurrentMediaNotificationKey() {
752         return null;
753     }
754 
755     @Override
getGroupManager()756     public NotificationGroupManager getGroupManager() {
757         return mGroupManager;
758     }
759 
760     /**
761      * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
762      * @param action A dismiss action that is called if it's safe to start the activity.
763      * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
764      */
dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone)765     protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
766         action.onDismiss();
767     }
768 
769     @Override
onConfigurationChanged(Configuration newConfig)770     protected void onConfigurationChanged(Configuration newConfig) {
771         final Locale locale = mContext.getResources().getConfiguration().locale;
772         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
773         final float fontScale = newConfig.fontScale;
774 
775         if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
776             if (DEBUG) {
777                 Log.v(TAG, String.format(
778                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
779                         locale, ld));
780             }
781             mLocale = locale;
782             mLayoutDirection = ld;
783             refreshLayout(ld);
784         }
785     }
786 
updateNotificationVetoButton(View row, StatusBarNotification n)787     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
788         View vetoButton = row.findViewById(R.id.veto);
789         if (n.isClearable()) {
790             final String _pkg = n.getPackageName();
791             final String _tag = n.getTag();
792             final int _id = n.getId();
793             final int _userId = n.getUserId();
794             vetoButton.setOnClickListener(new View.OnClickListener() {
795                     public void onClick(View v) {
796                         // Accessibility feedback
797                         v.announceForAccessibility(
798                                 mContext.getString(R.string.accessibility_notification_dismissed));
799                         try {
800                             mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
801 
802                         } catch (RemoteException ex) {
803                             // system process is dead if we're here.
804                         }
805                     }
806                 });
807             vetoButton.setVisibility(View.VISIBLE);
808         } else {
809             vetoButton.setVisibility(View.GONE);
810         }
811         vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
812         return vetoButton;
813     }
814 
815 
applyColorsAndBackgrounds(StatusBarNotification sbn, NotificationData.Entry entry)816     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
817             NotificationData.Entry entry) {
818 
819         if (entry.getContentView().getId()
820                 != com.android.internal.R.id.status_bar_latest_event_content) {
821             // Using custom RemoteViews
822             if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
823                     && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
824                 entry.row.setShowingLegacyBackground(true);
825                 entry.legacy = true;
826             }
827         } else {
828             // Using platform templates
829             final int color = sbn.getNotification().color;
830             if (isMediaNotification(entry)) {
831                 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
832                         ? mContext.getColor(
833                                 R.color.notification_material_background_media_default_color)
834                         : color);
835             }
836         }
837 
838         if (entry.icon != null) {
839             entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
840         }
841     }
842 
isMediaNotification(NotificationData.Entry entry)843     public boolean isMediaNotification(NotificationData.Entry entry) {
844         // TODO: confirm that there's a valid media key
845         return entry.getExpandedContentView() != null &&
846                entry.getExpandedContentView()
847                        .findViewById(com.android.internal.R.id.media_actions) != null;
848     }
849 
850     // The gear button in the guts that links to the app's own notification settings
startAppOwnNotificationSettingsActivity(Intent intent, final int notificationId, final String notificationTag, final int appUid)851     private void startAppOwnNotificationSettingsActivity(Intent intent,
852             final int notificationId, final String notificationTag, final int appUid) {
853         intent.putExtra("notification_id", notificationId);
854         intent.putExtra("notification_tag", notificationTag);
855         startNotificationGutsIntent(intent, appUid);
856     }
857 
858     // The (i) button in the guts that links to the system notification settings for that app
startAppNotificationSettingsActivity(String packageName, final int appUid)859     private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
860         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
861         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
862         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
863         startNotificationGutsIntent(intent, appUid);
864     }
865 
startNotificationGutsIntent(final Intent intent, final int appUid)866     private void startNotificationGutsIntent(final Intent intent, final int appUid) {
867         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
868         dismissKeyguardThenExecute(new OnDismissAction() {
869             @Override
870             public boolean onDismiss() {
871                 AsyncTask.execute(new Runnable() {
872                     public void run() {
873                         try {
874                             if (keyguardShowing) {
875                                 ActivityManagerNative.getDefault()
876                                         .keyguardWaitingForActivityDrawn();
877                             }
878                             TaskStackBuilder.create(mContext)
879                                     .addNextIntentWithParentStack(intent)
880                                     .startActivities(null,
881                                             new UserHandle(UserHandle.getUserId(appUid)));
882                             overrideActivityPendingAppTransition(keyguardShowing);
883                         } catch (RemoteException e) {
884                         }
885                     }
886                 });
887                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
888                 return true;
889             }
890         }, false /* afterKeyguardGone */);
891     }
892 
bindGuts(ExpandableNotificationRow row)893     private void bindGuts(ExpandableNotificationRow row) {
894         row.inflateGuts();
895         final StatusBarNotification sbn = row.getStatusBarNotification();
896         PackageManager pmUser = getPackageManagerForUser(
897                 sbn.getUser().getIdentifier());
898         row.setTag(sbn.getPackageName());
899         final View guts = row.getGuts();
900         final String pkg = sbn.getPackageName();
901         String appname = pkg;
902         Drawable pkgicon = null;
903         int appUid = -1;
904         try {
905             final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
906                     PackageManager.GET_UNINSTALLED_PACKAGES
907                             | PackageManager.GET_DISABLED_COMPONENTS);
908             if (info != null) {
909                 appname = String.valueOf(pmUser.getApplicationLabel(info));
910                 pkgicon = pmUser.getApplicationIcon(info);
911                 appUid = info.uid;
912             }
913         } catch (NameNotFoundException e) {
914             // app is gone, just show package name and generic icon
915             pkgicon = pmUser.getDefaultActivityIcon();
916         }
917         ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
918         ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
919         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
920         final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
921         final View appSettingsButton
922                 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
923         if (appUid >= 0) {
924             final int appUidF = appUid;
925             settingsButton.setOnClickListener(new View.OnClickListener() {
926                 public void onClick(View v) {
927                     MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_INFO);
928                     startAppNotificationSettingsActivity(pkg, appUidF);
929                 }
930             });
931 
932             final Intent appSettingsQueryIntent
933                     = new Intent(Intent.ACTION_MAIN)
934                     .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
935                     .setPackage(pkg);
936             List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
937             if (infos.size() > 0) {
938                 appSettingsButton.setVisibility(View.VISIBLE);
939                 appSettingsButton.setContentDescription(
940                         mContext.getResources().getString(
941                                 R.string.status_bar_notification_app_settings_title,
942                                 appname
943                         ));
944                 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
945                         .setClassName(pkg, infos.get(0).activityInfo.name);
946                 appSettingsButton.setOnClickListener(new View.OnClickListener() {
947                     public void onClick(View v) {
948                         MetricsLogger.action(mContext, MetricsLogger.ACTION_APP_NOTE_SETTINGS);
949                         startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
950                                 sbn.getId(),
951                                 sbn.getTag(),
952                                 appUidF);
953                     }
954                 });
955             } else {
956                 appSettingsButton.setVisibility(View.GONE);
957             }
958         } else {
959             settingsButton.setVisibility(View.GONE);
960             appSettingsButton.setVisibility(View.GONE);
961         }
962 
963     }
964 
getNotificationLongClicker()965     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
966         return new SwipeHelper.LongPressListener() {
967             @Override
968             public boolean onLongPress(View v, int x, int y) {
969                 dismissPopups();
970 
971                 if (!(v instanceof ExpandableNotificationRow)) {
972                     return false;
973                 }
974                 if (v.getWindowToken() == null) {
975                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
976                     return false;
977                 }
978 
979                 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
980                 bindGuts(row);
981 
982                 // Assume we are a status_bar_notification_row
983                 final NotificationGuts guts = row.getGuts();
984                 if (guts == null) {
985                     // This view has no guts. Examples are the more card or the dismiss all view
986                     return false;
987                 }
988 
989                 // Already showing?
990                 if (guts.getVisibility() == View.VISIBLE) {
991                     Log.e(TAG, "Trying to show notification guts, but already visible");
992                     return false;
993                 }
994 
995                 MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_CONTROLS);
996                 guts.setVisibility(View.VISIBLE);
997                 final double horz = Math.max(guts.getWidth() - x, x);
998                 final double vert = Math.max(guts.getActualHeight() - y, y);
999                 final float r = (float) Math.hypot(horz, vert);
1000                 final Animator a
1001                         = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
1002                 a.setDuration(400);
1003                 a.setInterpolator(mLinearOutSlowIn);
1004                 a.start();
1005 
1006                 mNotificationGutsExposed = guts;
1007 
1008                 return true;
1009             }
1010         };
1011     }
1012 
1013     public void dismissPopups() {
1014         if (mNotificationGutsExposed != null) {
1015             final NotificationGuts v = mNotificationGutsExposed;
1016             mNotificationGutsExposed = null;
1017 
1018             if (v.getWindowToken() == null) return;
1019 
1020             final int x = (v.getLeft() + v.getRight()) / 2;
1021             final int y = (v.getTop() + v.getActualHeight() / 2);
1022             final Animator a = ViewAnimationUtils.createCircularReveal(v,
1023                     x, y, x, 0);
1024             a.setDuration(200);
1025             a.setInterpolator(mFastOutLinearIn);
1026             a.addListener(new AnimatorListenerAdapter() {
1027                 @Override
1028                 public void onAnimationEnd(Animator animation) {
1029                     super.onAnimationEnd(animation);
1030                     v.setVisibility(View.GONE);
1031                 }
1032             });
1033             a.start();
1034         }
1035     }
1036 
1037     @Override
1038     public void showRecentApps(boolean triggeredFromAltTab) {
1039         int msg = MSG_SHOW_RECENT_APPS;
1040         mHandler.removeMessages(msg);
1041         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
1042     }
1043 
1044     @Override
1045     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1046         int msg = MSG_HIDE_RECENT_APPS;
1047         mHandler.removeMessages(msg);
1048         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1049                 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1050     }
1051 
1052     @Override
1053     public void toggleRecentApps() {
1054         toggleRecents();
1055     }
1056 
1057     @Override
1058     public void preloadRecentApps() {
1059         int msg = MSG_PRELOAD_RECENT_APPS;
1060         mHandler.removeMessages(msg);
1061         mHandler.sendEmptyMessage(msg);
1062     }
1063 
1064     @Override
1065     public void cancelPreloadRecentApps() {
1066         int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1067         mHandler.removeMessages(msg);
1068         mHandler.sendEmptyMessage(msg);
1069     }
1070 
1071     /** Jumps to the next affiliated task in the group. */
1072     public void showNextAffiliatedTask() {
1073         int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1074         mHandler.removeMessages(msg);
1075         mHandler.sendEmptyMessage(msg);
1076     }
1077 
1078     /** Jumps to the previous affiliated task in the group. */
1079     public void showPreviousAffiliatedTask() {
1080         int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1081         mHandler.removeMessages(msg);
1082         mHandler.sendEmptyMessage(msg);
1083     }
1084 
1085     protected H createHandler() {
1086          return new H();
1087     }
1088 
1089     static void sendCloseSystemWindows(Context context, String reason) {
1090         if (ActivityManagerNative.isSystemReady()) {
1091             try {
1092                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1093             } catch (RemoteException e) {
1094             }
1095         }
1096     }
1097 
1098     protected abstract View getStatusBarView();
1099 
1100     protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1101         // additional optimization when we have software system buttons - start loading the recent
1102         // tasks on touch down
1103         @Override
1104         public boolean onTouch(View v, MotionEvent event) {
1105             int action = event.getAction() & MotionEvent.ACTION_MASK;
1106             if (action == MotionEvent.ACTION_DOWN) {
1107                 preloadRecents();
1108             } else if (action == MotionEvent.ACTION_CANCEL) {
1109                 cancelPreloadingRecents();
1110             } else if (action == MotionEvent.ACTION_UP) {
1111                 if (!v.isPressed()) {
1112                     cancelPreloadingRecents();
1113                 }
1114 
1115             }
1116             return false;
1117         }
1118     };
1119 
1120     /** Proxy for RecentsComponent */
1121 
1122     protected void showRecents(boolean triggeredFromAltTab) {
1123         if (mRecents != null) {
1124             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1125             mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
1126         }
1127     }
1128 
1129     protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1130         if (mRecents != null) {
1131             mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1132         }
1133     }
1134 
1135     protected void toggleRecents() {
1136         if (mRecents != null) {
1137             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1138             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
1139         }
1140     }
1141 
1142     protected void preloadRecents() {
1143         if (mRecents != null) {
1144             mRecents.preloadRecents();
1145         }
1146     }
1147 
1148     protected void cancelPreloadingRecents() {
1149         if (mRecents != null) {
1150             mRecents.cancelPreloadingRecents();
1151         }
1152     }
1153 
1154     protected void showRecentsNextAffiliatedTask() {
1155         if (mRecents != null) {
1156             mRecents.showNextAffiliatedTask();
1157         }
1158     }
1159 
1160     protected void showRecentsPreviousAffiliatedTask() {
1161         if (mRecents != null) {
1162             mRecents.showPrevAffiliatedTask();
1163         }
1164     }
1165 
1166     @Override
1167     public void onVisibilityChanged(boolean visible) {
1168         // Do nothing
1169     }
1170 
1171     /**
1172      * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1173      */
1174     public abstract void maybeEscalateHeadsUp();
1175 
1176     /**
1177      * Save the current "public" (locked and secure) state of the lockscreen.
1178      */
1179     public void setLockscreenPublicMode(boolean publicMode) {
1180         mLockscreenPublicMode = publicMode;
1181     }
1182 
1183     public boolean isLockscreenPublicMode() {
1184         return mLockscreenPublicMode;
1185     }
1186 
1187     /**
1188      * Has the given user chosen to allow their private (full) notifications to be shown even
1189      * when the lockscreen is in "public" (secure & locked) mode?
1190      */
1191     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1192         if (userHandle == UserHandle.USER_ALL) {
1193             return true;
1194         }
1195 
1196         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1197             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
1198                     mContext.getContentResolver(),
1199                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1200             final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1201                     userHandle);
1202             final boolean allowedByDpm = (dpmFlags
1203                     & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1204             final boolean allowed = allowedByUser && allowedByDpm;
1205             mUsersAllowingPrivateNotifications.append(userHandle, allowed);
1206             return allowed;
1207         }
1208 
1209         return mUsersAllowingPrivateNotifications.get(userHandle);
1210     }
1211 
1212     /**
1213      * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
1214      * notification data. If so, private notifications should show their (possibly
1215      * auto-generated) publicVersion, and secret notifications should be totally invisible.
1216      */
1217     @Override  // NotificationData.Environment
1218     public boolean shouldHideSensitiveContents(int userid) {
1219         return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
1220     }
1221 
1222     public void onNotificationClear(StatusBarNotification notification) {
1223         try {
1224             mBarService.onNotificationClear(
1225                     notification.getPackageName(),
1226                     notification.getTag(),
1227                     notification.getId(),
1228                     notification.getUserId());
1229         } catch (android.os.RemoteException ex) {
1230             // oh well
1231         }
1232     }
1233 
1234     protected class H extends Handler {
1235         public void handleMessage(Message m) {
1236             switch (m.what) {
1237              case MSG_SHOW_RECENT_APPS:
1238                  showRecents(m.arg1 > 0);
1239                  break;
1240              case MSG_HIDE_RECENT_APPS:
1241                  hideRecents(m.arg1 > 0, m.arg2 > 0);
1242                  break;
1243              case MSG_TOGGLE_RECENTS_APPS:
1244                  toggleRecents();
1245                  break;
1246              case MSG_PRELOAD_RECENT_APPS:
1247                   preloadRecents();
1248                   break;
1249              case MSG_CANCEL_PRELOAD_RECENT_APPS:
1250                   cancelPreloadingRecents();
1251                   break;
1252              case MSG_SHOW_NEXT_AFFILIATED_TASK:
1253                   showRecentsNextAffiliatedTask();
1254                   break;
1255              case MSG_SHOW_PREV_AFFILIATED_TASK:
1256                   showRecentsPreviousAffiliatedTask();
1257                   break;
1258             }
1259         }
1260     }
1261 
1262     protected void workAroundBadLayerDrawableOpacity(View v) {
1263     }
1264 
1265     protected boolean inflateViews(Entry entry, ViewGroup parent) {
1266         PackageManager pmUser = getPackageManagerForUser(
1267                 entry.notification.getUser().getIdentifier());
1268 
1269         int maxHeight = mRowMaxHeight;
1270         final StatusBarNotification sbn = entry.notification;
1271         RemoteViews contentView = sbn.getNotification().contentView;
1272         RemoteViews bigContentView = sbn.getNotification().bigContentView;
1273         RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
1274 
1275         if (contentView == null) {
1276             return false;
1277         }
1278 
1279         if (DEBUG) {
1280             Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
1281         }
1282 
1283         Notification publicNotification = sbn.getNotification().publicVersion;
1284 
1285         ExpandableNotificationRow row;
1286 
1287         // Stash away previous user expansion state so we can restore it at
1288         // the end.
1289         boolean hasUserChangedExpansion = false;
1290         boolean userExpanded = false;
1291         boolean userLocked = false;
1292 
1293         if (entry.row != null) {
1294             row = entry.row;
1295             hasUserChangedExpansion = row.hasUserChangedExpansion();
1296             userExpanded = row.isUserExpanded();
1297             userLocked = row.isUserLocked();
1298             entry.reset();
1299             if (hasUserChangedExpansion) {
1300                 row.setUserExpanded(userExpanded);
1301             }
1302         } else {
1303             // create the row view
1304             LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1305                     Context.LAYOUT_INFLATER_SERVICE);
1306             row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1307                     parent, false);
1308             row.setExpansionLogger(this, entry.notification.getKey());
1309             row.setGroupManager(mGroupManager);
1310         }
1311 
1312         workAroundBadLayerDrawableOpacity(row);
1313         View vetoButton = updateNotificationVetoButton(row, sbn);
1314         vetoButton.setContentDescription(mContext.getString(
1315                 R.string.accessibility_remove_notification));
1316 
1317         // NB: the large icon is now handled entirely by the template
1318 
1319         // bind the click event to the content area
1320         NotificationContentView contentContainer = row.getPrivateLayout();
1321         NotificationContentView contentContainerPublic = row.getPublicLayout();
1322 
1323         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1324 
1325         mNotificationClicker.register(row, sbn);
1326 
1327         // set up the adaptive layout
1328         View contentViewLocal = null;
1329         View bigContentViewLocal = null;
1330         View headsUpContentViewLocal = null;
1331         try {
1332             contentViewLocal = contentView.apply(
1333                     sbn.getPackageContext(mContext),
1334                     contentContainer,
1335                     mOnClickHandler);
1336             if (bigContentView != null) {
1337                 bigContentViewLocal = bigContentView.apply(
1338                         sbn.getPackageContext(mContext),
1339                         contentContainer,
1340                         mOnClickHandler);
1341             }
1342             if (headsUpContentView != null) {
1343                 headsUpContentViewLocal = headsUpContentView.apply(
1344                         sbn.getPackageContext(mContext),
1345                         contentContainer,
1346                         mOnClickHandler);
1347             }
1348         }
1349         catch (RuntimeException e) {
1350             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1351             Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1352             return false;
1353         }
1354 
1355         if (contentViewLocal != null) {
1356             contentViewLocal.setIsRootNamespace(true);
1357             contentContainer.setContractedChild(contentViewLocal);
1358         }
1359         if (bigContentViewLocal != null) {
1360             bigContentViewLocal.setIsRootNamespace(true);
1361             contentContainer.setExpandedChild(bigContentViewLocal);
1362         }
1363         if (headsUpContentViewLocal != null) {
1364             headsUpContentViewLocal.setIsRootNamespace(true);
1365             contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1366         }
1367 
1368         // now the public version
1369         View publicViewLocal = null;
1370         if (publicNotification != null) {
1371             try {
1372                 publicViewLocal = publicNotification.contentView.apply(
1373                         sbn.getPackageContext(mContext),
1374                         contentContainerPublic, mOnClickHandler);
1375 
1376                 if (publicViewLocal != null) {
1377                     publicViewLocal.setIsRootNamespace(true);
1378                     contentContainerPublic.setContractedChild(publicViewLocal);
1379                 }
1380             }
1381             catch (RuntimeException e) {
1382                 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1383                 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
1384                 publicViewLocal = null;
1385             }
1386         }
1387 
1388         // Extract target SDK version.
1389         try {
1390             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1391             entry.targetSdk = info.targetSdkVersion;
1392         } catch (NameNotFoundException ex) {
1393             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1394         }
1395 
1396         if (publicViewLocal == null) {
1397             // Add a basic notification template
1398             publicViewLocal = LayoutInflater.from(mContext).inflate(
1399                     R.layout.notification_public_default,
1400                     contentContainerPublic, false);
1401             publicViewLocal.setIsRootNamespace(true);
1402 
1403             final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
1404             try {
1405                 title.setText(pmUser.getApplicationLabel(
1406                         pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
1407             } catch (NameNotFoundException e) {
1408                 title.setText(entry.notification.getPackageName());
1409             }
1410 
1411             final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
1412             final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
1413                     R.id.profile_badge_line3);
1414 
1415             final StatusBarIcon ic = new StatusBarIcon(
1416                     entry.notification.getUser(),
1417                     entry.notification.getPackageName(),
1418                     entry.notification.getNotification().getSmallIcon(),
1419                     entry.notification.getNotification().iconLevel,
1420                     entry.notification.getNotification().number,
1421                     entry.notification.getNotification().tickerText);
1422 
1423             Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
1424             icon.setImageDrawable(iconDrawable);
1425             if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
1426                     || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
1427                 icon.setBackgroundResource(
1428                         com.android.internal.R.drawable.notification_icon_legacy_bg);
1429                 int padding = mContext.getResources().getDimensionPixelSize(
1430                         com.android.internal.R.dimen.notification_large_icon_circle_padding);
1431                 icon.setPadding(padding, padding, padding, padding);
1432                 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
1433                     icon.getBackground().setColorFilter(
1434                             sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
1435                 }
1436             }
1437 
1438             if (profileBadge != null) {
1439                 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
1440                         entry.notification.getUser(), 0);
1441                 if (profileDrawable != null) {
1442                     profileBadge.setImageDrawable(profileDrawable);
1443                     profileBadge.setVisibility(View.VISIBLE);
1444                 } else {
1445                     profileBadge.setVisibility(View.GONE);
1446                 }
1447             }
1448 
1449             final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
1450             final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
1451             if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
1452                 time.setVisibility(View.VISIBLE);
1453                 time.setTime(entry.notification.getNotification().when);
1454             }
1455 
1456             final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
1457             if (text != null) {
1458                 text.setText(R.string.notification_hidden_text);
1459                 text.setTextAppearance(mContext,
1460                         R.style.TextAppearance_Material_Notification_Parenthetical);
1461             }
1462 
1463             int topPadding = Notification.Builder.calculateTopPadding(mContext,
1464                     false /* hasThreeLines */,
1465                     mContext.getResources().getConfiguration().fontScale);
1466             title.setPadding(0, topPadding, 0, 0);
1467 
1468             contentContainerPublic.setContractedChild(publicViewLocal);
1469             entry.autoRedacted = true;
1470         }
1471 
1472         if (MULTIUSER_DEBUG) {
1473             TextView debug = (TextView) row.findViewById(R.id.debug_info);
1474             if (debug != null) {
1475                 debug.setVisibility(View.VISIBLE);
1476                 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1477             }
1478         }
1479         entry.row = row;
1480         entry.row.setHeightRange(mRowMinHeight, maxHeight);
1481         entry.row.setOnActivatedListener(this);
1482         entry.row.setExpandable(bigContentViewLocal != null);
1483 
1484         applyColorsAndBackgrounds(sbn, entry);
1485 
1486         // Restore previous flags.
1487         if (hasUserChangedExpansion) {
1488             // Note: setUserExpanded() conveniently ignores calls with
1489             //       userExpanded=true if !isExpandable().
1490             row.setUserExpanded(userExpanded);
1491         }
1492         row.setUserLocked(userLocked);
1493         row.setStatusBarNotification(entry.notification);
1494 
1495         return true;
1496     }
1497 
1498     private final class NotificationClicker implements View.OnClickListener {
1499         public void onClick(final View v) {
1500             if (!(v instanceof ExpandableNotificationRow)) {
1501                 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
1502                 return;
1503             }
1504 
1505             final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1506             final StatusBarNotification sbn = row.getStatusBarNotification();
1507             if (sbn == null) {
1508                 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
1509                 return;
1510             }
1511 
1512             final PendingIntent intent = sbn.getNotification().contentIntent;
1513             final String notificationKey = sbn.getKey();
1514 
1515             if (NOTIFICATION_CLICK_DEBUG) {
1516                 Log.d(TAG, "Clicked on content of " + notificationKey);
1517             }
1518             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1519             final boolean afterKeyguardGone = intent.isActivity()
1520                     && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1521                             mCurrentUserId);
1522             dismissKeyguardThenExecute(new OnDismissAction() {
1523                 public boolean onDismiss() {
1524                     if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1525                         // Release the HUN notification to the shade.
1526                         //
1527                         // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1528                         // become canceled shortly by NoMan, but we can't assume that.
1529                         HeadsUpManager.setIsClickedNotification(row, true);
1530                         mHeadsUpManager.releaseImmediately(notificationKey);
1531                     }
1532                     new Thread() {
1533                         @Override
1534                         public void run() {
1535                             try {
1536                                 if (keyguardShowing && !afterKeyguardGone) {
1537                                     ActivityManagerNative.getDefault()
1538                                             .keyguardWaitingForActivityDrawn();
1539                                 }
1540 
1541                                 // The intent we are sending is for the application, which
1542                                 // won't have permission to immediately start an activity after
1543                                 // the user switches to home.  We know it is safe to do at this
1544                                 // point, so make sure new activity switches are now allowed.
1545                                 ActivityManagerNative.getDefault().resumeAppSwitches();
1546                             } catch (RemoteException e) {
1547                             }
1548 
1549                             if (intent != null) {
1550                                 try {
1551                                     intent.send();
1552                                 } catch (PendingIntent.CanceledException e) {
1553                                     // the stack trace isn't very helpful here.
1554                                     // Just log the exception message.
1555                                     Log.w(TAG, "Sending contentIntent failed: " + e);
1556 
1557                                     // TODO: Dismiss Keyguard.
1558                                 }
1559                                 if (intent.isActivity()) {
1560                                     mAssistManager.hideAssist();
1561                                     overrideActivityPendingAppTransition(keyguardShowing
1562                                             && !afterKeyguardGone);
1563                                 }
1564                             }
1565 
1566                             try {
1567                                 mBarService.onNotificationClick(notificationKey);
1568                             } catch (RemoteException ex) {
1569                                 // system process is dead if we're here.
1570                             }
1571                         }
1572                     }.start();
1573 
1574                     // close the shade if it was open
1575                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1576                             true /* force */, true /* delayed */);
1577                     visibilityChanged(false);
1578 
1579                     return true;
1580                 }
1581             }, afterKeyguardGone);
1582         }
1583 
1584         public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
1585             final PendingIntent contentIntent = sbn.getNotification().contentIntent;
1586             if (contentIntent != null) {
1587                 row.setOnClickListener(this);
1588             } else {
1589                 row.setOnClickListener(null);
1590             }
1591         }
1592     }
1593 
1594     public void animateCollapsePanels(int flags, boolean force) {
1595     }
1596 
1597     public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
1598     }
1599 
1600     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
1601         if (keyguardShowing) {
1602             try {
1603                 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
1604             } catch (RemoteException e) {
1605                 Log.w(TAG, "Error overriding app transition: " + e);
1606             }
1607         }
1608     }
1609 
1610     protected void visibilityChanged(boolean visible) {
1611         if (mVisible != visible) {
1612             mVisible = visible;
1613             if (!visible) {
1614                 dismissPopups();
1615             }
1616         }
1617         updateVisibleToUser();
1618     }
1619 
1620     protected void updateVisibleToUser() {
1621         boolean oldVisibleToUser = mVisibleToUser;
1622         mVisibleToUser = mVisible && mScreenOnFromKeyguard;
1623 
1624         if (oldVisibleToUser != mVisibleToUser) {
1625             handleVisibleToUserChanged(mVisibleToUser);
1626         }
1627     }
1628 
1629     /**
1630      * The LEDs are turned off when the notification panel is shown, even just a little bit.
1631      */
1632     protected void handleVisibleToUserChanged(boolean visibleToUser) {
1633         try {
1634             if (visibleToUser) {
1635                 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
1636                 boolean clearNotificationEffects =
1637                     ((mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD) ||
1638                             (!pinnedHeadsUp && (mState == StatusBarState.SHADE
1639                                     || mState == StatusBarState.SHADE_LOCKED)));
1640                 int notificationLoad = mNotificationData.getActiveNotifications().size();
1641                 if (pinnedHeadsUp && isPanelFullyCollapsed())  {
1642                     notificationLoad = 1;
1643                 } else {
1644                     MetricsLogger.histogram(mContext, "note_load", notificationLoad);
1645                 }
1646                 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
1647             } else {
1648                 mBarService.onPanelHidden();
1649             }
1650         } catch (RemoteException ex) {
1651             // Won't fail unless the world has ended.
1652         }
1653     }
1654 
1655     /**
1656      * Clear Buzz/Beep/Blink.
1657      */
1658     public void clearNotificationEffects() {
1659         try {
1660             mBarService.clearNotificationEffects();
1661         } catch (RemoteException e) {
1662             // Won't fail unless the world has ended.
1663         }
1664     }
1665 
1666     protected abstract boolean isPanelFullyCollapsed();
1667 
1668     /**
1669      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1670      * about the failure.
1671      *
1672      * WARNING: this will call back into us.  Don't hold any locks.
1673      */
1674     void handleNotificationError(StatusBarNotification n, String message) {
1675         removeNotification(n.getKey(), null);
1676         try {
1677             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
1678                     n.getInitialPid(), message, n.getUserId());
1679         } catch (RemoteException ex) {
1680             // The end is nigh.
1681         }
1682     }
1683 
1684     protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
1685         NotificationData.Entry entry = mNotificationData.remove(key, ranking);
1686         if (entry == null) {
1687             Log.w(TAG, "removeNotification for unknown key: " + key);
1688             return null;
1689         }
1690         updateNotifications();
1691         return entry.notification;
1692     }
1693 
1694     protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
1695         if (DEBUG) {
1696             Log.d(TAG, "createNotificationViews(notification=" + sbn);
1697         }
1698         final StatusBarIconView iconView = createIcon(sbn);
1699         if (iconView == null) {
1700             return null;
1701         }
1702 
1703         // Construct the expanded view.
1704         NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
1705         if (!inflateViews(entry, mStackScroller)) {
1706             handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
1707             return null;
1708         }
1709         return entry;
1710     }
1711 
1712     protected StatusBarIconView createIcon(StatusBarNotification sbn) {
1713         // Construct the icon.
1714         Notification n = sbn.getNotification();
1715         final StatusBarIconView iconView = new StatusBarIconView(mContext,
1716                 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
1717         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1718 
1719         final Icon smallIcon = n.getSmallIcon();
1720         if (smallIcon == null) {
1721             handleNotificationError(sbn,
1722                     "No small icon in notification from " + sbn.getPackageName());
1723             return null;
1724         }
1725         final StatusBarIcon ic = new StatusBarIcon(
1726                 sbn.getUser(),
1727                 sbn.getPackageName(),
1728                 smallIcon,
1729                 n.iconLevel,
1730                 n.number,
1731                 n.tickerText);
1732         if (!iconView.set(ic)) {
1733             handleNotificationError(sbn, "Couldn't create icon: " + ic);
1734             return null;
1735         }
1736         return iconView;
1737     }
1738 
1739     protected void addNotificationViews(Entry entry, RankingMap ranking) {
1740         if (entry == null) {
1741             return;
1742         }
1743         // Add the expanded view and icon.
1744         mNotificationData.add(entry, ranking);
1745         updateNotifications();
1746     }
1747 
1748     /**
1749      * @return The number of notifications we show on Keyguard.
1750      */
1751     protected abstract int getMaxKeyguardNotifications();
1752 
1753     /**
1754      * Updates expanded, dimmed and locked states of notification rows.
1755      */
1756     protected void updateRowStates() {
1757         int maxKeyguardNotifications = getMaxKeyguardNotifications();
1758         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
1759 
1760         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1761         final int N = activeNotifications.size();
1762 
1763         int visibleNotifications = 0;
1764         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
1765         for (int i = 0; i < N; i++) {
1766             NotificationData.Entry entry = activeNotifications.get(i);
1767             if (onKeyguard) {
1768                 entry.row.setExpansionDisabled(true);
1769             } else {
1770                 entry.row.setExpansionDisabled(false);
1771                 if (!entry.row.isUserLocked()) {
1772                     boolean top = (i == 0);
1773                     entry.row.setSystemExpanded(top);
1774                 }
1775             }
1776             boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
1777             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
1778             if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
1779                     (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
1780                             || !showOnKeyguard || isInvisibleChild))) {
1781                 entry.row.setVisibility(View.GONE);
1782                 if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
1783                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
1784                 }
1785             } else {
1786                 boolean wasGone = entry.row.getVisibility() == View.GONE;
1787                 entry.row.setVisibility(View.VISIBLE);
1788                 if (!isInvisibleChild) {
1789                     if (wasGone) {
1790                         // notify the scroller of a child addition
1791                         mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
1792                     }
1793                     visibleNotifications++;
1794                 }
1795             }
1796         }
1797 
1798         mStackScroller.updateOverflowContainerVisibility(onKeyguard
1799                 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
1800 
1801         mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
1802         mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
1803         mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
1804                 mStackScroller.getChildCount() - 3);
1805     }
1806 
1807     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1808         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
1809     }
1810 
1811     protected void setZenMode(int mode) {
1812         if (!isDeviceProvisioned()) return;
1813         mZenMode = mode;
1814         updateNotifications();
1815     }
1816 
1817     // extended in PhoneStatusBar
1818     protected void setShowLockscreenNotifications(boolean show) {
1819         mShowLockscreenNotifications = show;
1820     }
1821 
1822     private void updateLockscreenNotificationSetting() {
1823         final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1824                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1825                 1,
1826                 mCurrentUserId) != 0;
1827         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
1828                 null /* admin */, mCurrentUserId);
1829         final boolean allowedByDpm = (dpmFlags
1830                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
1831         setShowLockscreenNotifications(show && allowedByDpm);
1832     }
1833 
1834     protected abstract void setAreThereNotifications();
1835     protected abstract void updateNotifications();
1836     public abstract boolean shouldDisableNavbarGestures();
1837 
1838     public abstract void addNotification(StatusBarNotification notification,
1839             RankingMap ranking, Entry oldEntry);
1840     protected abstract void updateNotificationRanking(RankingMap ranking);
1841     public abstract void removeNotification(String key, RankingMap ranking);
1842 
1843     public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
1844         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
1845 
1846         final String key = notification.getKey();
1847         Entry entry = mNotificationData.get(key);
1848         if (entry == null) {
1849             return;
1850         }
1851 
1852         Notification n = notification.getNotification();
1853         if (DEBUG) {
1854             logUpdate(entry, n);
1855         }
1856         boolean applyInPlace = shouldApplyInPlace(entry, n);
1857         boolean shouldInterrupt = shouldInterrupt(entry, notification);
1858         boolean alertAgain = alertAgain(entry, n);
1859 
1860         entry.notification = notification;
1861         mGroupManager.onEntryUpdated(entry, entry.notification);
1862 
1863         boolean updateSuccessful = false;
1864         if (applyInPlace) {
1865             if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
1866             try {
1867                 if (entry.icon != null) {
1868                     // Update the icon
1869                     final StatusBarIcon ic = new StatusBarIcon(
1870                             notification.getUser(),
1871                             notification.getPackageName(),
1872                             n.getSmallIcon(),
1873                             n.iconLevel,
1874                             n.number,
1875                             n.tickerText);
1876                     entry.icon.setNotification(n);
1877                     if (!entry.icon.set(ic)) {
1878                         handleNotificationError(notification, "Couldn't update icon: " + ic);
1879                         return;
1880                     }
1881                 }
1882                 updateNotificationViews(entry, notification);
1883                 updateSuccessful = true;
1884             }
1885             catch (RuntimeException e) {
1886                 // It failed to apply cleanly.
1887                 Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
1888             }
1889         }
1890         if (!updateSuccessful) {
1891             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1892             final StatusBarIcon ic = new StatusBarIcon(
1893                     notification.getUser(),
1894                     notification.getPackageName(),
1895                     n.getSmallIcon(),
1896                     n.iconLevel,
1897                     n.number,
1898                     n.tickerText);
1899             entry.icon.setNotification(n);
1900             entry.icon.set(ic);
1901             inflateViews(entry, mStackScroller);
1902         }
1903         updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
1904         mNotificationData.updateRanking(ranking);
1905         updateNotifications();
1906 
1907         // Update the veto button accordingly (and as a result, whether this row is
1908         // swipe-dismissable)
1909         updateNotificationVetoButton(entry.row, notification);
1910 
1911         if (DEBUG) {
1912             // Is this for you?
1913             boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
1914             Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
1915         }
1916 
1917         setAreThereNotifications();
1918     }
1919 
1920     protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
1921             boolean alertAgain);
1922 
1923     private void logUpdate(Entry oldEntry, Notification n) {
1924         StatusBarNotification oldNotification = oldEntry.notification;
1925         Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
1926                 + " ongoing=" + oldNotification.isOngoing()
1927                 + " expanded=" + oldEntry.getContentView()
1928                 + " contentView=" + oldNotification.getNotification().contentView
1929                 + " bigContentView=" + oldNotification.getNotification().bigContentView
1930                 + " publicView=" + oldNotification.getNotification().publicVersion
1931                 + " rowParent=" + oldEntry.row.getParent());
1932         Log.d(TAG, "new notification: when=" + n.when
1933                 + " ongoing=" + oldNotification.isOngoing()
1934                 + " contentView=" + n.contentView
1935                 + " bigContentView=" + n.bigContentView
1936                 + " publicView=" + n.publicVersion);
1937     }
1938 
1939     /**
1940      * @return whether we can just reapply the RemoteViews from a notification in-place when it is
1941      * updated
1942      */
1943     private boolean shouldApplyInPlace(Entry entry, Notification n) {
1944         StatusBarNotification oldNotification = entry.notification;
1945         // XXX: modify when we do something more intelligent with the two content views
1946         final RemoteViews oldContentView = oldNotification.getNotification().contentView;
1947         final RemoteViews contentView = n.contentView;
1948         final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
1949         final RemoteViews bigContentView = n.bigContentView;
1950         final RemoteViews oldHeadsUpContentView
1951                 = oldNotification.getNotification().headsUpContentView;
1952         final RemoteViews headsUpContentView = n.headsUpContentView;
1953         final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
1954         final RemoteViews oldPublicContentView = oldPublicNotification != null
1955                 ? oldPublicNotification.contentView : null;
1956         final Notification publicNotification = n.publicVersion;
1957         final RemoteViews publicContentView = publicNotification != null
1958                 ? publicNotification.contentView : null;
1959         boolean contentsUnchanged = entry.getContentView() != null
1960                 && contentView.getPackage() != null
1961                 && oldContentView.getPackage() != null
1962                 && oldContentView.getPackage().equals(contentView.getPackage())
1963                 && oldContentView.getLayoutId() == contentView.getLayoutId();
1964         // large view may be null
1965         boolean bigContentsUnchanged =
1966                 (entry.getExpandedContentView() == null && bigContentView == null)
1967                 || ((entry.getExpandedContentView() != null && bigContentView != null)
1968                     && bigContentView.getPackage() != null
1969                     && oldBigContentView.getPackage() != null
1970                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
1971                     && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
1972         boolean headsUpContentsUnchanged =
1973                 (oldHeadsUpContentView == null && headsUpContentView == null)
1974                 || ((oldHeadsUpContentView != null && headsUpContentView != null)
1975                     && headsUpContentView.getPackage() != null
1976                     && oldHeadsUpContentView.getPackage() != null
1977                     && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
1978                     && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
1979         boolean publicUnchanged  =
1980                 (oldPublicContentView == null && publicContentView == null)
1981                 || ((oldPublicContentView != null && publicContentView != null)
1982                         && publicContentView.getPackage() != null
1983                         && oldPublicContentView.getPackage() != null
1984                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
1985                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
1986         return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
1987                 && publicUnchanged;
1988     }
1989 
1990     private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
1991         final RemoteViews contentView = notification.getNotification().contentView;
1992         final RemoteViews bigContentView = notification.getNotification().bigContentView;
1993         final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
1994         final Notification publicVersion = notification.getNotification().publicVersion;
1995         final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
1996                 : null;
1997 
1998         // Reapply the RemoteViews
1999         contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
2000         if (bigContentView != null && entry.getExpandedContentView() != null) {
2001             bigContentView.reapply(notification.getPackageContext(mContext),
2002                     entry.getExpandedContentView(),
2003                     mOnClickHandler);
2004         }
2005         View headsUpChild = entry.getHeadsUpContentView();
2006         if (headsUpContentView != null && headsUpChild != null) {
2007             headsUpContentView.reapply(notification.getPackageContext(mContext),
2008                     headsUpChild, mOnClickHandler);
2009         }
2010         if (publicContentView != null && entry.getPublicContentView() != null) {
2011             publicContentView.reapply(notification.getPackageContext(mContext),
2012                     entry.getPublicContentView(), mOnClickHandler);
2013         }
2014         // update the contentIntent
2015         mNotificationClicker.register(entry.row, notification);
2016 
2017         entry.row.setStatusBarNotification(notification);
2018         entry.row.notifyContentUpdated();
2019         entry.row.resetHeight();
2020     }
2021 
2022     protected void notifyHeadsUpScreenOff() {
2023         maybeEscalateHeadsUp();
2024     }
2025 
2026     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2027         return oldEntry == null || !oldEntry.hasInterrupted()
2028                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2029     }
2030 
2031     protected boolean shouldInterrupt(Entry entry) {
2032         return shouldInterrupt(entry, entry.notification);
2033     }
2034 
2035     protected boolean shouldInterrupt(Entry entry, StatusBarNotification sbn) {
2036         if (mNotificationData.shouldFilterOut(sbn)) {
2037             if (DEBUG) {
2038                 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
2039             }
2040             return false;
2041         }
2042 
2043         if (isSnoozedPackage(sbn)) {
2044             return false;
2045         }
2046 
2047         Notification notification = sbn.getNotification();
2048         // some predicates to make the boolean logic legible
2049         boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
2050                 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
2051                 || notification.sound != null
2052                 || notification.vibrate != null;
2053         boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
2054         boolean isFullscreen = notification.fullScreenIntent != null;
2055         boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
2056         boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
2057                 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
2058         boolean accessibilityForcesLaunch = isFullscreen
2059                 && mAccessibilityManager.isTouchExplorationEnabled();
2060         boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent();
2061 
2062         boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
2063                 && isAllowed
2064                 && !accessibilityForcesLaunch
2065                 && !justLaunchedFullScreenIntent
2066                 && mPowerManager.isScreenOn()
2067                 && (!mStatusBarKeyguardViewManager.isShowing()
2068                         || mStatusBarKeyguardViewManager.isOccluded())
2069                 && !mStatusBarKeyguardViewManager.isInputRestricted();
2070         try {
2071             interrupt = interrupt && !mDreamManager.isDreaming();
2072         } catch (RemoteException e) {
2073             Log.d(TAG, "failed to query dream manager", e);
2074         }
2075         if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
2076         return interrupt;
2077     }
2078 
2079     protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2080 
2081     public void setInteracting(int barWindow, boolean interacting) {
2082         // hook for subclasses
2083     }
2084 
2085     public void setBouncerShowing(boolean bouncerShowing) {
2086         mBouncerShowing = bouncerShowing;
2087     }
2088 
2089     /**
2090      * @return Whether the security bouncer from Keyguard is showing.
2091      */
2092     public boolean isBouncerShowing() {
2093         return mBouncerShowing;
2094     }
2095 
2096     public void destroy() {
2097         mContext.unregisterReceiver(mBroadcastReceiver);
2098         try {
2099             mNotificationListener.unregisterAsSystemService();
2100         } catch (RemoteException e) {
2101             // Ignore.
2102         }
2103     }
2104 
2105     /**
2106      * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2107      *         return PackageManager for mContext
2108      */
2109     protected PackageManager getPackageManagerForUser(int userId) {
2110         Context contextForUser = mContext;
2111         // UserHandle defines special userId as negative values, e.g. USER_ALL
2112         if (userId >= 0) {
2113             try {
2114                 // Create a context for the correct user so if a package isn't installed
2115                 // for user 0 we can still load information about the package.
2116                 contextForUser =
2117                         mContext.createPackageContextAsUser(mContext.getPackageName(),
2118                         Context.CONTEXT_RESTRICTED,
2119                         new UserHandle(userId));
2120             } catch (NameNotFoundException e) {
2121                 // Shouldn't fail to find the package name for system ui.
2122             }
2123         }
2124         return contextForUser.getPackageManager();
2125     }
2126 
2127     @Override
2128     public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2129         try {
2130             mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2131         } catch (RemoteException e) {
2132             // Ignore.
2133         }
2134     }
2135 
2136     public boolean isKeyguardSecure() {
2137         if (mStatusBarKeyguardViewManager == null) {
2138             // startKeyguard() hasn't been called yet, so we don't know.
2139             // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2140             // value onVisibilityChanged().
2141             Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2142                     new Throwable());
2143             return false;
2144         }
2145         return mStatusBarKeyguardViewManager.isSecure();
2146     }
2147 
2148     @Override
2149     public void showAssistDisclosure() {
2150         if (mAssistManager != null) {
2151             mAssistManager.showDisclosure();
2152         }
2153     }
2154 
2155     @Override
2156     public void startAssist(Bundle args) {
2157         if (mAssistManager != null) {
2158             mAssistManager.startAssist(args);
2159         }
2160     }
2161 }
2162