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