NotificationManagerService.java revision 57627794b682235afd60adfb235fee46d32f54e2
1/*
2 * Copyright (C) 2007 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
17package com.android.server.notification;
18
19import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
20import static android.service.notification.NotificationListenerService.TRIM_FULL;
21import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
22import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
23import static org.xmlpull.v1.XmlPullParser.END_TAG;
24import static org.xmlpull.v1.XmlPullParser.START_TAG;
25
26import android.app.ActivityManager;
27import android.app.ActivityManagerNative;
28import android.app.AppGlobals;
29import android.app.AppOpsManager;
30import android.app.IActivityManager;
31import android.app.INotificationManager;
32import android.app.ITransientNotification;
33import android.app.Notification;
34import android.app.NotificationManager;
35import android.app.PendingIntent;
36import android.app.StatusBarManager;
37import android.content.BroadcastReceiver;
38import android.content.ComponentName;
39import android.content.ContentResolver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.pm.ApplicationInfo;
44import android.content.pm.IPackageManager;
45import android.content.pm.PackageInfo;
46import android.content.pm.PackageManager;
47import android.content.pm.PackageManager.NameNotFoundException;
48import android.content.pm.ParceledListSlice;
49import android.content.res.Resources;
50import android.database.ContentObserver;
51import android.media.AudioAttributes;
52import android.media.AudioManager;
53import android.media.AudioSystem;
54import android.media.IRingtonePlayer;
55import android.net.Uri;
56import android.os.Binder;
57import android.os.Bundle;
58import android.os.Environment;
59import android.os.Handler;
60import android.os.HandlerThread;
61import android.os.IBinder;
62import android.os.IInterface;
63import android.os.Looper;
64import android.os.Message;
65import android.os.Process;
66import android.os.RemoteException;
67import android.os.UserHandle;
68import android.os.Vibrator;
69import android.provider.Settings;
70import android.service.notification.Condition;
71import android.service.notification.IConditionListener;
72import android.service.notification.IConditionProvider;
73import android.service.notification.INotificationListener;
74import android.service.notification.IStatusBarNotificationHolder;
75import android.service.notification.NotificationListenerService;
76import android.service.notification.NotificationRankingUpdate;
77import android.service.notification.StatusBarNotification;
78import android.service.notification.ZenModeConfig;
79import android.telephony.PhoneStateListener;
80import android.telephony.TelephonyManager;
81import android.text.TextUtils;
82import android.util.ArrayMap;
83import android.util.ArraySet;
84import android.util.AtomicFile;
85import android.util.Log;
86import android.util.Slog;
87import android.util.Xml;
88import android.view.accessibility.AccessibilityEvent;
89import android.view.accessibility.AccessibilityManager;
90import android.widget.Toast;
91
92import com.android.internal.R;
93import com.android.internal.util.FastXmlSerializer;
94import com.android.server.EventLogTags;
95import com.android.server.SystemService;
96import com.android.server.lights.Light;
97import com.android.server.lights.LightsManager;
98import com.android.server.notification.ManagedServices.ManagedServiceInfo;
99import com.android.server.notification.ManagedServices.UserProfiles;
100import com.android.server.statusbar.StatusBarManagerInternal;
101
102import libcore.io.IoUtils;
103
104import org.xmlpull.v1.XmlPullParser;
105import org.xmlpull.v1.XmlPullParserException;
106import org.xmlpull.v1.XmlSerializer;
107
108import java.io.File;
109import java.io.FileDescriptor;
110import java.io.FileInputStream;
111import java.io.FileNotFoundException;
112import java.io.FileOutputStream;
113import java.io.IOException;
114import java.io.PrintWriter;
115import java.util.ArrayDeque;
116import java.util.ArrayList;
117import java.util.HashSet;
118import java.util.Iterator;
119import java.util.Map.Entry;
120import java.util.NoSuchElementException;
121import java.util.Objects;
122
123/** {@hide} */
124public class NotificationManagerService extends SystemService {
125    static final String TAG = "NotificationService";
126    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
127
128    static final int MAX_PACKAGE_NOTIFICATIONS = 50;
129
130    // message codes
131    static final int MESSAGE_TIMEOUT = 2;
132    static final int MESSAGE_SAVE_POLICY_FILE = 3;
133    static final int MESSAGE_RECONSIDER_RANKING = 4;
134    static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
135    static final int MESSAGE_SEND_RANKING_UPDATE = 6;
136    static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
137    static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
138
139    static final int LONG_DELAY = 3500; // 3.5 seconds
140    static final int SHORT_DELAY = 2000; // 2 seconds
141
142    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
143
144    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
145
146    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
147    static final boolean SCORE_ONGOING_HIGHER = false;
148
149    static final int JUNK_SCORE = -1000;
150    static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
151    static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
152
153    // Notifications with scores below this will not interrupt the user, either via LED or
154    // sound or vibration
155    static final int SCORE_INTERRUPTION_THRESHOLD =
156            Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
157
158    static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
159    static final boolean ENABLE_BLOCKED_TOASTS = true;
160
161    // When #matchesCallFilter is called from the ringer, wait at most
162    // 3s to resolve the contacts. This timeout is required since
163    // ContactsProvider might take a long time to start up.
164    //
165    // Return STARRED_CONTACT when the timeout is hit in order to avoid
166    // missed calls in ZEN mode "Important".
167    static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
168    static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
169            ValidateNotificationPeople.STARRED_CONTACT;
170
171    /** notification_enqueue status value for a newly enqueued notification. */
172    private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
173
174    /** notification_enqueue status value for an existing notification. */
175    private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
176
177    /** notification_enqueue status value for an ignored notification. */
178    private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
179
180    private IActivityManager mAm;
181    AudioManager mAudioManager;
182    StatusBarManagerInternal mStatusBar;
183    Vibrator mVibrator;
184
185    final IBinder mForegroundToken = new Binder();
186    private WorkerHandler mHandler;
187    private final HandlerThread mRankingThread = new HandlerThread("ranker",
188            Process.THREAD_PRIORITY_BACKGROUND);
189
190    private Light mNotificationLight;
191    Light mAttentionLight;
192    private int mDefaultNotificationColor;
193    private int mDefaultNotificationLedOn;
194
195    private int mDefaultNotificationLedOff;
196    private long[] mDefaultVibrationPattern;
197
198    private long[] mFallbackVibrationPattern;
199    private boolean mUseAttentionLight;
200    boolean mSystemReady;
201
202    private boolean mDisableNotificationEffects;
203    private int mCallState;
204    private String mSoundNotificationKey;
205    private String mVibrateNotificationKey;
206
207    private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
208    private ComponentName mEffectsSuppressor;
209    private int mListenerHints;  // right now, all hints are global
210    private int mInterruptionFilter;  // current ZEN mode as communicated to listeners
211
212    // for enabling and disabling notification pulse behavior
213    private boolean mScreenOn = true;
214    private boolean mInCall = false;
215    private boolean mNotificationPulseEnabled;
216
217    // used as a mutex for access to all active notifications & listeners
218    final ArrayList<NotificationRecord> mNotificationList =
219            new ArrayList<NotificationRecord>();
220    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
221            new ArrayMap<String, NotificationRecord>();
222    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
223    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
224
225    // The last key in this list owns the hardware.
226    ArrayList<String> mLights = new ArrayList<>();
227
228    private AppOpsManager mAppOps;
229
230    private Archive mArchive;
231
232    // Notification control database. For now just contains disabled packages.
233    private AtomicFile mPolicyFile;
234    private HashSet<String> mBlockedPackages = new HashSet<String>();
235
236    private static final int DB_VERSION = 1;
237
238    private static final String TAG_BODY = "notification-policy";
239    private static final String ATTR_VERSION = "version";
240
241    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
242    private static final String TAG_PACKAGE = "package";
243    private static final String ATTR_NAME = "name";
244
245    private RankingHelper mRankingHelper;
246
247    private final UserProfiles mUserProfiles = new UserProfiles();
248    private NotificationListeners mListeners;
249    private ConditionProviders mConditionProviders;
250    private NotificationUsageStats mUsageStats;
251
252    private static final int MY_UID = Process.myUid();
253    private static final int MY_PID = Process.myPid();
254    private static final int REASON_DELEGATE_CLICK = 1;
255    private static final int REASON_DELEGATE_CANCEL = 2;
256    private static final int REASON_DELEGATE_CANCEL_ALL = 3;
257    private static final int REASON_DELEGATE_ERROR = 4;
258    private static final int REASON_PACKAGE_CHANGED = 5;
259    private static final int REASON_USER_STOPPED = 6;
260    private static final int REASON_PACKAGE_BANNED = 7;
261    private static final int REASON_NOMAN_CANCEL = 8;
262    private static final int REASON_NOMAN_CANCEL_ALL = 9;
263    private static final int REASON_LISTENER_CANCEL = 10;
264    private static final int REASON_LISTENER_CANCEL_ALL = 11;
265    private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
266    private static final int REASON_GROUP_OPTIMIZATION = 13;
267
268    private static class Archive {
269        final int mBufferSize;
270        final ArrayDeque<StatusBarNotification> mBuffer;
271
272        public Archive(int size) {
273            mBufferSize = size;
274            mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
275        }
276
277        public String toString() {
278            final StringBuilder sb = new StringBuilder();
279            final int N = mBuffer.size();
280            sb.append("Archive (");
281            sb.append(N);
282            sb.append(" notification");
283            sb.append((N==1)?")":"s)");
284            return sb.toString();
285        }
286
287        public void record(StatusBarNotification nr) {
288            if (mBuffer.size() == mBufferSize) {
289                mBuffer.removeFirst();
290            }
291
292            // We don't want to store the heavy bits of the notification in the archive,
293            // but other clients in the system process might be using the object, so we
294            // store a (lightened) copy.
295            mBuffer.addLast(nr.cloneLight());
296        }
297
298        public void clear() {
299            mBuffer.clear();
300        }
301
302        public Iterator<StatusBarNotification> descendingIterator() {
303            return mBuffer.descendingIterator();
304        }
305        public Iterator<StatusBarNotification> ascendingIterator() {
306            return mBuffer.iterator();
307        }
308        public Iterator<StatusBarNotification> filter(
309                final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
310            return new Iterator<StatusBarNotification>() {
311                StatusBarNotification mNext = findNext();
312
313                private StatusBarNotification findNext() {
314                    while (iter.hasNext()) {
315                        StatusBarNotification nr = iter.next();
316                        if ((pkg == null || nr.getPackageName() == pkg)
317                                && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
318                            return nr;
319                        }
320                    }
321                    return null;
322                }
323
324                @Override
325                public boolean hasNext() {
326                    return mNext == null;
327                }
328
329                @Override
330                public StatusBarNotification next() {
331                    StatusBarNotification next = mNext;
332                    if (next == null) {
333                        throw new NoSuchElementException();
334                    }
335                    mNext = findNext();
336                    return next;
337                }
338
339                @Override
340                public void remove() {
341                    iter.remove();
342                }
343            };
344        }
345
346        public StatusBarNotification[] getArray(int count) {
347            if (count == 0) count = mBufferSize;
348            final StatusBarNotification[] a
349                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
350            Iterator<StatusBarNotification> iter = descendingIterator();
351            int i=0;
352            while (iter.hasNext() && i < count) {
353                a[i++] = iter.next();
354            }
355            return a;
356        }
357
358        public StatusBarNotification[] getArray(int count, String pkg, int userId) {
359            if (count == 0) count = mBufferSize;
360            final StatusBarNotification[] a
361                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
362            Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
363            int i=0;
364            while (iter.hasNext() && i < count) {
365                a[i++] = iter.next();
366            }
367            return a;
368        }
369
370    }
371
372    private void loadPolicyFile() {
373        synchronized(mPolicyFile) {
374            mBlockedPackages.clear();
375
376            FileInputStream infile = null;
377            try {
378                infile = mPolicyFile.openRead();
379                final XmlPullParser parser = Xml.newPullParser();
380                parser.setInput(infile, null);
381
382                int type;
383                String tag;
384                int version = DB_VERSION;
385                while ((type = parser.next()) != END_DOCUMENT) {
386                    tag = parser.getName();
387                    if (type == START_TAG) {
388                        if (TAG_BODY.equals(tag)) {
389                            version = Integer.parseInt(
390                                    parser.getAttributeValue(null, ATTR_VERSION));
391                        } else if (TAG_BLOCKED_PKGS.equals(tag)) {
392                            while ((type = parser.next()) != END_DOCUMENT) {
393                                tag = parser.getName();
394                                if (TAG_PACKAGE.equals(tag)) {
395                                    mBlockedPackages.add(
396                                            parser.getAttributeValue(null, ATTR_NAME));
397                                } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
398                                    break;
399                                }
400                            }
401                        }
402                    }
403                    mZenModeHelper.readXml(parser);
404                    mRankingHelper.readXml(parser);
405                }
406            } catch (FileNotFoundException e) {
407                // No data yet
408            } catch (IOException e) {
409                Log.wtf(TAG, "Unable to read notification policy", e);
410            } catch (NumberFormatException e) {
411                Log.wtf(TAG, "Unable to parse notification policy", e);
412            } catch (XmlPullParserException e) {
413                Log.wtf(TAG, "Unable to parse notification policy", e);
414            } finally {
415                IoUtils.closeQuietly(infile);
416            }
417        }
418    }
419
420    public void savePolicyFile() {
421        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
422        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
423    }
424
425    private void handleSavePolicyFile() {
426        Slog.d(TAG, "handleSavePolicyFile");
427        synchronized (mPolicyFile) {
428            final FileOutputStream stream;
429            try {
430                stream = mPolicyFile.startWrite();
431            } catch (IOException e) {
432                Slog.w(TAG, "Failed to save policy file", e);
433                return;
434            }
435
436            try {
437                final XmlSerializer out = new FastXmlSerializer();
438                out.setOutput(stream, "utf-8");
439                out.startDocument(null, true);
440                out.startTag(null, TAG_BODY);
441                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
442                mZenModeHelper.writeXml(out);
443                mRankingHelper.writeXml(out);
444                out.endTag(null, TAG_BODY);
445                out.endDocument();
446                mPolicyFile.finishWrite(stream);
447            } catch (IOException e) {
448                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
449                mPolicyFile.failWrite(stream);
450            }
451        }
452    }
453
454    /** Use this when you actually want to post a notification or toast.
455     *
456     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
457     */
458    private boolean noteNotificationOp(String pkg, int uid) {
459        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
460                != AppOpsManager.MODE_ALLOWED) {
461            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
462            return false;
463        }
464        return true;
465    }
466
467    private static final class ToastRecord
468    {
469        final int pid;
470        final String pkg;
471        final ITransientNotification callback;
472        int duration;
473
474        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
475        {
476            this.pid = pid;
477            this.pkg = pkg;
478            this.callback = callback;
479            this.duration = duration;
480        }
481
482        void update(int duration) {
483            this.duration = duration;
484        }
485
486        void dump(PrintWriter pw, String prefix, DumpFilter filter) {
487            if (filter != null && !filter.matches(pkg)) return;
488            pw.println(prefix + this);
489        }
490
491        @Override
492        public final String toString()
493        {
494            return "ToastRecord{"
495                + Integer.toHexString(System.identityHashCode(this))
496                + " pkg=" + pkg
497                + " callback=" + callback
498                + " duration=" + duration;
499        }
500    }
501
502    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
503
504        @Override
505        public void onSetDisabled(int status) {
506            synchronized (mNotificationList) {
507                mDisableNotificationEffects =
508                        (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
509                if (disableNotificationEffects(null) != null) {
510                    // cancel whatever's going on
511                    long identity = Binder.clearCallingIdentity();
512                    try {
513                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
514                        if (player != null) {
515                            player.stopAsync();
516                        }
517                    } catch (RemoteException e) {
518                    } finally {
519                        Binder.restoreCallingIdentity(identity);
520                    }
521
522                    identity = Binder.clearCallingIdentity();
523                    try {
524                        mVibrator.cancel();
525                    } finally {
526                        Binder.restoreCallingIdentity(identity);
527                    }
528                }
529            }
530        }
531
532        @Override
533        public void onClearAll(int callingUid, int callingPid, int userId) {
534            synchronized (mNotificationList) {
535                cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
536                        /*includeCurrentProfiles*/ true);
537            }
538        }
539
540        @Override
541        public void onNotificationClick(int callingUid, int callingPid, String key) {
542            synchronized (mNotificationList) {
543                EventLogTags.writeNotificationClicked(key);
544                NotificationRecord r = mNotificationsByKey.get(key);
545                if (r == null) {
546                    Log.w(TAG, "No notification with key: " + key);
547                    return;
548                }
549                StatusBarNotification sbn = r.sbn;
550                cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
551                        sbn.getId(), Notification.FLAG_AUTO_CANCEL,
552                        Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
553                        REASON_DELEGATE_CLICK, null);
554            }
555        }
556
557        @Override
558        public void onNotificationActionClick(int callingUid, int callingPid, String key,
559                int actionIndex) {
560            synchronized (mNotificationList) {
561                EventLogTags.writeNotificationActionClicked(key, actionIndex);
562                NotificationRecord r = mNotificationsByKey.get(key);
563                if (r == null) {
564                    Log.w(TAG, "No notification with key: " + key);
565                    return;
566                }
567                // TODO: Log action click via UsageStats.
568            }
569        }
570
571        @Override
572        public void onNotificationClear(int callingUid, int callingPid,
573                String pkg, String tag, int id, int userId) {
574            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
575                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
576                    true, userId, REASON_DELEGATE_CANCEL, null);
577        }
578
579        @Override
580        public void onPanelRevealed(boolean clearEffects) {
581            EventLogTags.writeNotificationPanelRevealed();
582            if (clearEffects) {
583                clearEffects();
584            }
585        }
586
587        @Override
588        public void onPanelHidden() {
589            EventLogTags.writeNotificationPanelHidden();
590        }
591
592        @Override
593        public void clearEffects() {
594            synchronized (mNotificationList) {
595                if (DBG) Slog.d(TAG, "clearEffects");
596
597                // sound
598                mSoundNotificationKey = null;
599
600                long identity = Binder.clearCallingIdentity();
601                try {
602                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
603                    if (player != null) {
604                        player.stopAsync();
605                    }
606                } catch (RemoteException e) {
607                } finally {
608                    Binder.restoreCallingIdentity(identity);
609                }
610
611                // vibrate
612                mVibrateNotificationKey = null;
613                identity = Binder.clearCallingIdentity();
614                try {
615                    mVibrator.cancel();
616                } finally {
617                    Binder.restoreCallingIdentity(identity);
618                }
619
620                // light
621                mLights.clear();
622                updateLightsLocked();
623            }
624        }
625
626        @Override
627        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
628                int uid, int initialPid, String message, int userId) {
629            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
630                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
631            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
632                    REASON_DELEGATE_ERROR, null);
633            long ident = Binder.clearCallingIdentity();
634            try {
635                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
636                        "Bad notification posted from package " + pkg
637                        + ": " + message);
638            } catch (RemoteException e) {
639            }
640            Binder.restoreCallingIdentity(ident);
641        }
642
643        @Override
644        public void onNotificationVisibilityChanged(
645                String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
646            // Using ';' as separator since eventlogs uses ',' to separate
647            // args.
648            EventLogTags.writeNotificationVisibilityChanged(
649                    TextUtils.join(";", newlyVisibleKeys),
650                    TextUtils.join(";", noLongerVisibleKeys));
651            synchronized (mNotificationList) {
652                for (String key : newlyVisibleKeys) {
653                    NotificationRecord r = mNotificationsByKey.get(key);
654                    if (r == null) continue;
655                    r.stats.onVisibilityChanged(true);
656                }
657                // Note that we might receive this event after notifications
658                // have already left the system, e.g. after dismissing from the
659                // shade. Hence not finding notifications in
660                // mNotificationsByKey is not an exceptional condition.
661                for (String key : noLongerVisibleKeys) {
662                    NotificationRecord r = mNotificationsByKey.get(key);
663                    if (r == null) continue;
664                    r.stats.onVisibilityChanged(false);
665                }
666            }
667        }
668
669        @Override
670        public void onNotificationExpansionChanged(String key,
671                boolean userAction, boolean expanded) {
672            EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0);
673            synchronized (mNotificationList) {
674                NotificationRecord r = mNotificationsByKey.get(key);
675                if (r != null) {
676                    r.stats.onExpansionChanged(userAction, expanded);
677                }
678            }
679        }
680    };
681
682    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
683        @Override
684        public void onReceive(Context context, Intent intent) {
685            String action = intent.getAction();
686
687            boolean queryRestart = false;
688            boolean queryRemove = false;
689            boolean packageChanged = false;
690            boolean cancelNotifications = true;
691
692            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
693                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
694                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
695                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
696                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
697                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
698                int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
699                        UserHandle.USER_ALL);
700                String pkgList[] = null;
701                boolean queryReplace = queryRemove &&
702                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
703                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
704                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
705                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
706                } else if (queryRestart) {
707                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
708                } else {
709                    Uri uri = intent.getData();
710                    if (uri == null) {
711                        return;
712                    }
713                    String pkgName = uri.getSchemeSpecificPart();
714                    if (pkgName == null) {
715                        return;
716                    }
717                    if (packageChanged) {
718                        // We cancel notifications for packages which have just been disabled
719                        try {
720                            final IPackageManager pm = AppGlobals.getPackageManager();
721                            final int enabled = pm.getApplicationEnabledSetting(pkgName,
722                                    changeUserId != UserHandle.USER_ALL ? changeUserId :
723                                    UserHandle.USER_OWNER);
724                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
725                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
726                                cancelNotifications = false;
727                            }
728                        } catch (IllegalArgumentException e) {
729                            // Package doesn't exist; probably racing with uninstall.
730                            // cancelNotifications is already true, so nothing to do here.
731                            if (DBG) {
732                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
733                            }
734                        } catch (RemoteException e) {
735                            // Failed to talk to PackageManagerService Should never happen!
736                        }
737                    }
738                    pkgList = new String[]{pkgName};
739                }
740
741                if (pkgList != null && (pkgList.length > 0)) {
742                    for (String pkgName : pkgList) {
743                        if (cancelNotifications) {
744                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
745                                    changeUserId, REASON_PACKAGE_CHANGED, null);
746                        }
747                    }
748                }
749                mListeners.onPackagesChanged(queryReplace, pkgList);
750                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
751            }
752        }
753    };
754
755    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
756        @Override
757        public void onReceive(Context context, Intent intent) {
758            String action = intent.getAction();
759
760            if (action.equals(Intent.ACTION_SCREEN_ON)) {
761                // Keep track of screen on/off state, but do not turn off the notification light
762                // until user passes through the lock screen or views the notification.
763                mScreenOn = true;
764                updateNotificationPulse();
765            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
766                mScreenOn = false;
767                updateNotificationPulse();
768            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
769                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
770                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
771                updateNotificationPulse();
772            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
773                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
774                if (userHandle >= 0) {
775                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
776                            REASON_USER_STOPPED, null);
777                }
778            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
779                // turn off LED when user passes through lock screen
780                mNotificationLight.turnOff();
781                mStatusBar.notificationLightOff();
782            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
783                // reload per-user settings
784                mSettingsObserver.update(null);
785                mUserProfiles.updateCache(context);
786                // Refresh managed services
787                mConditionProviders.onUserSwitched();
788                mListeners.onUserSwitched();
789            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
790                mUserProfiles.updateCache(context);
791            }
792        }
793    };
794
795    class SettingsObserver extends ContentObserver {
796        private final Uri NOTIFICATION_LIGHT_PULSE_URI
797                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
798
799        SettingsObserver(Handler handler) {
800            super(handler);
801        }
802
803        void observe() {
804            ContentResolver resolver = getContext().getContentResolver();
805            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
806                    false, this, UserHandle.USER_ALL);
807            update(null);
808        }
809
810        @Override public void onChange(boolean selfChange, Uri uri) {
811            update(uri);
812        }
813
814        public void update(Uri uri) {
815            ContentResolver resolver = getContext().getContentResolver();
816            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
817                boolean pulseEnabled = Settings.System.getInt(resolver,
818                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
819                if (mNotificationPulseEnabled != pulseEnabled) {
820                    mNotificationPulseEnabled = pulseEnabled;
821                    updateNotificationPulse();
822                }
823            }
824        }
825    }
826
827    private SettingsObserver mSettingsObserver;
828    private ZenModeHelper mZenModeHelper;
829
830    private final Runnable mBuzzBeepBlinked = new Runnable() {
831        @Override
832        public void run() {
833            mStatusBar.buzzBeepBlinked();
834        }
835    };
836
837    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
838        int[] ar = r.getIntArray(resid);
839        if (ar == null) {
840            return def;
841        }
842        final int len = ar.length > maxlen ? maxlen : ar.length;
843        long[] out = new long[len];
844        for (int i=0; i<len; i++) {
845            out[i] = ar[i];
846        }
847        return out;
848    }
849
850    public NotificationManagerService(Context context) {
851        super(context);
852    }
853
854    @Override
855    public void onStart() {
856        Resources resources = getContext().getResources();
857
858        mAm = ActivityManagerNative.getDefault();
859        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
860        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
861
862        mHandler = new WorkerHandler();
863        mRankingThread.start();
864        String[] extractorNames;
865        try {
866            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
867        } catch (Resources.NotFoundException e) {
868            extractorNames = new String[0];
869        }
870        mRankingHelper = new RankingHelper(getContext(),
871                new RankingWorkerHandler(mRankingThread.getLooper()),
872                extractorNames);
873        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper());
874        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
875            @Override
876            public void onConfigChanged() {
877                savePolicyFile();
878            }
879
880            @Override
881            void onZenModeChanged() {
882                synchronized(mNotificationList) {
883                    updateInterruptionFilterLocked();
884                }
885            }
886        });
887        final File systemDir = new File(Environment.getDataDirectory(), "system");
888        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
889        mUsageStats = new NotificationUsageStats(getContext());
890
891        importOldBlockDb();
892
893        mListeners = new NotificationListeners();
894        mConditionProviders = new ConditionProviders(getContext(),
895                mHandler, mUserProfiles, mZenModeHelper);
896        mStatusBar = getLocalService(StatusBarManagerInternal.class);
897        mStatusBar.setNotificationDelegate(mNotificationDelegate);
898
899        final LightsManager lights = getLocalService(LightsManager.class);
900        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
901        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
902
903        mDefaultNotificationColor = resources.getColor(
904                R.color.config_defaultNotificationColor);
905        mDefaultNotificationLedOn = resources.getInteger(
906                R.integer.config_defaultNotificationLedOn);
907        mDefaultNotificationLedOff = resources.getInteger(
908                R.integer.config_defaultNotificationLedOff);
909
910        mDefaultVibrationPattern = getLongArray(resources,
911                R.array.config_defaultNotificationVibePattern,
912                VIBRATE_PATTERN_MAXLEN,
913                DEFAULT_VIBRATE_PATTERN);
914
915        mFallbackVibrationPattern = getLongArray(resources,
916                R.array.config_notificationFallbackVibePattern,
917                VIBRATE_PATTERN_MAXLEN,
918                DEFAULT_VIBRATE_PATTERN);
919
920        mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
921
922        // Don't start allowing notifications until the setup wizard has run once.
923        // After that, including subsequent boots, init with notifications turned on.
924        // This works on the first boot because the setup wizard will toggle this
925        // flag at least once and we'll go back to 0 after that.
926        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
927                    Settings.Global.DEVICE_PROVISIONED, 0)) {
928            mDisableNotificationEffects = true;
929        }
930        mZenModeHelper.readZenModeFromSetting();
931
932        mUserProfiles.updateCache(getContext());
933        listenForCallState();
934
935        // register for various Intents
936        IntentFilter filter = new IntentFilter();
937        filter.addAction(Intent.ACTION_SCREEN_ON);
938        filter.addAction(Intent.ACTION_SCREEN_OFF);
939        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
940        filter.addAction(Intent.ACTION_USER_PRESENT);
941        filter.addAction(Intent.ACTION_USER_STOPPED);
942        filter.addAction(Intent.ACTION_USER_SWITCHED);
943        filter.addAction(Intent.ACTION_USER_ADDED);
944        getContext().registerReceiver(mIntentReceiver, filter);
945
946        IntentFilter pkgFilter = new IntentFilter();
947        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
948        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
949        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
950        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
951        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
952        pkgFilter.addDataScheme("package");
953        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
954                null);
955
956        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
957        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
958                null);
959
960        mSettingsObserver = new SettingsObserver(mHandler);
961
962        mArchive = new Archive(resources.getInteger(
963                R.integer.config_notificationServiceArchiveSize));
964
965        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
966        publishLocalService(NotificationManagerInternal.class, mInternalService);
967    }
968
969    /**
970     * Read the old XML-based app block database and import those blockages into the AppOps system.
971     */
972    private void importOldBlockDb() {
973        loadPolicyFile();
974
975        PackageManager pm = getContext().getPackageManager();
976        for (String pkg : mBlockedPackages) {
977            PackageInfo info = null;
978            try {
979                info = pm.getPackageInfo(pkg, 0);
980                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
981            } catch (NameNotFoundException e) {
982                // forget you
983            }
984        }
985        mBlockedPackages.clear();
986    }
987
988    @Override
989    public void onBootPhase(int phase) {
990        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
991            // no beeping until we're basically done booting
992            mSystemReady = true;
993
994            // Grab our optional AudioService
995            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
996            mZenModeHelper.onSystemReady();
997        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
998            // This observer will force an update when observe is called, causing us to
999            // bind to listener services.
1000            mSettingsObserver.observe();
1001            mListeners.onBootPhaseAppsCanStart();
1002            mConditionProviders.onBootPhaseAppsCanStart();
1003        }
1004    }
1005
1006    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1007        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1008
1009        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1010                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1011
1012        // Now, cancel any outstanding notifications that are part of a just-disabled app
1013        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1014            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1015                    REASON_PACKAGE_BANNED, null);
1016        }
1017    }
1018
1019    private void updateListenerHintsLocked() {
1020        final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
1021        if (hints == mListenerHints) return;
1022        mListenerHints = hints;
1023        scheduleListenerHintsChanged(hints);
1024    }
1025
1026    private void updateEffectsSuppressorLocked() {
1027        final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1028                ? mListenersDisablingEffects.valueAt(0).component : null;
1029        if (Objects.equals(suppressor, mEffectsSuppressor)) return;
1030        mEffectsSuppressor = suppressor;
1031        mZenModeHelper.setEffectsSuppressed(suppressor != null);
1032        getContext().sendBroadcast(new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)
1033                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
1034    }
1035
1036    private void updateInterruptionFilterLocked() {
1037        int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1038        if (interruptionFilter == mInterruptionFilter) return;
1039        mInterruptionFilter = interruptionFilter;
1040        scheduleInterruptionFilterChanged(interruptionFilter);
1041    }
1042
1043    private final IBinder mService = new INotificationManager.Stub() {
1044        // Toasts
1045        // ============================================================================
1046
1047        @Override
1048        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1049        {
1050            if (DBG) {
1051                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1052                        + " duration=" + duration);
1053            }
1054
1055            if (pkg == null || callback == null) {
1056                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1057                return ;
1058            }
1059
1060            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1061
1062            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1063                if (!isSystemToast) {
1064                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1065                    return;
1066                }
1067            }
1068
1069            synchronized (mToastQueue) {
1070                int callingPid = Binder.getCallingPid();
1071                long callingId = Binder.clearCallingIdentity();
1072                try {
1073                    ToastRecord record;
1074                    int index = indexOfToastLocked(pkg, callback);
1075                    // If it's already in the queue, we update it in place, we don't
1076                    // move it to the end of the queue.
1077                    if (index >= 0) {
1078                        record = mToastQueue.get(index);
1079                        record.update(duration);
1080                    } else {
1081                        // Limit the number of toasts that any given package except the android
1082                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1083                        if (!isSystemToast) {
1084                            int count = 0;
1085                            final int N = mToastQueue.size();
1086                            for (int i=0; i<N; i++) {
1087                                 final ToastRecord r = mToastQueue.get(i);
1088                                 if (r.pkg.equals(pkg)) {
1089                                     count++;
1090                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1091                                         Slog.e(TAG, "Package has already posted " + count
1092                                                + " toasts. Not showing more. Package=" + pkg);
1093                                         return;
1094                                     }
1095                                 }
1096                            }
1097                        }
1098
1099                        record = new ToastRecord(callingPid, pkg, callback, duration);
1100                        mToastQueue.add(record);
1101                        index = mToastQueue.size() - 1;
1102                        keepProcessAliveLocked(callingPid);
1103                    }
1104                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1105                    // new or just been updated.  Call back and tell it to show itself.
1106                    // If the callback fails, this will remove it from the list, so don't
1107                    // assume that it's valid after this.
1108                    if (index == 0) {
1109                        showNextToastLocked();
1110                    }
1111                } finally {
1112                    Binder.restoreCallingIdentity(callingId);
1113                }
1114            }
1115        }
1116
1117        @Override
1118        public void cancelToast(String pkg, ITransientNotification callback) {
1119            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1120
1121            if (pkg == null || callback == null) {
1122                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1123                return ;
1124            }
1125
1126            synchronized (mToastQueue) {
1127                long callingId = Binder.clearCallingIdentity();
1128                try {
1129                    int index = indexOfToastLocked(pkg, callback);
1130                    if (index >= 0) {
1131                        cancelToastLocked(index);
1132                    } else {
1133                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1134                                + " callback=" + callback);
1135                    }
1136                } finally {
1137                    Binder.restoreCallingIdentity(callingId);
1138                }
1139            }
1140        }
1141
1142        @Override
1143        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1144                Notification notification, int[] idOut, int userId) throws RemoteException {
1145            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1146                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1147        }
1148
1149        @Override
1150        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1151            checkCallerIsSystemOrSameApp(pkg);
1152            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1153                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1154            // Don't allow client applications to cancel foreground service notis.
1155            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1156                    Binder.getCallingUid() == Process.SYSTEM_UID
1157                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1158                    null);
1159        }
1160
1161        @Override
1162        public void cancelAllNotifications(String pkg, int userId) {
1163            checkCallerIsSystemOrSameApp(pkg);
1164
1165            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1166                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1167
1168            // Calling from user space, don't allow the canceling of actively
1169            // running foreground services.
1170            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1171                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1172                    REASON_NOMAN_CANCEL_ALL, null);
1173        }
1174
1175        @Override
1176        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1177            checkCallerIsSystem();
1178
1179            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1180        }
1181
1182        /**
1183         * Use this when you just want to know if notifications are OK for this package.
1184         */
1185        @Override
1186        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1187            checkCallerIsSystem();
1188            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1189                    == AppOpsManager.MODE_ALLOWED);
1190        }
1191
1192        @Override
1193        public void setPackagePriority(String pkg, int uid, int priority) {
1194            checkCallerIsSystem();
1195            mRankingHelper.setPackagePriority(pkg, uid, priority);
1196            savePolicyFile();
1197        }
1198
1199        @Override
1200        public int getPackagePriority(String pkg, int uid) {
1201            checkCallerIsSystem();
1202            return mRankingHelper.getPackagePriority(pkg, uid);
1203        }
1204
1205        @Override
1206        public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
1207            checkCallerIsSystem();
1208            mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
1209            savePolicyFile();
1210        }
1211
1212        @Override
1213        public int getPackageVisibilityOverride(String pkg, int uid) {
1214            checkCallerIsSystem();
1215            return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
1216        }
1217
1218        /**
1219         * System-only API for getting a list of current (i.e. not cleared) notifications.
1220         *
1221         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1222         * @returns A list of all the notifications, in natural order.
1223         */
1224        @Override
1225        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1226            // enforce() will ensure the calling uid has the correct permission
1227            getContext().enforceCallingOrSelfPermission(
1228                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1229                    "NotificationManagerService.getActiveNotifications");
1230
1231            StatusBarNotification[] tmp = null;
1232            int uid = Binder.getCallingUid();
1233
1234            // noteOp will check to make sure the callingPkg matches the uid
1235            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1236                    == AppOpsManager.MODE_ALLOWED) {
1237                synchronized (mNotificationList) {
1238                    tmp = new StatusBarNotification[mNotificationList.size()];
1239                    final int N = mNotificationList.size();
1240                    for (int i=0; i<N; i++) {
1241                        tmp[i] = mNotificationList.get(i).sbn;
1242                    }
1243                }
1244            }
1245            return tmp;
1246        }
1247
1248        /**
1249         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1250         *
1251         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1252         */
1253        @Override
1254        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1255            // enforce() will ensure the calling uid has the correct permission
1256            getContext().enforceCallingOrSelfPermission(
1257                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1258                    "NotificationManagerService.getHistoricalNotifications");
1259
1260            StatusBarNotification[] tmp = null;
1261            int uid = Binder.getCallingUid();
1262
1263            // noteOp will check to make sure the callingPkg matches the uid
1264            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1265                    == AppOpsManager.MODE_ALLOWED) {
1266                synchronized (mArchive) {
1267                    tmp = mArchive.getArray(count);
1268                }
1269            }
1270            return tmp;
1271        }
1272
1273        /**
1274         * Register a listener binder directly with the notification manager.
1275         *
1276         * Only works with system callers. Apps should extend
1277         * {@link android.service.notification.NotificationListenerService}.
1278         */
1279        @Override
1280        public void registerListener(final INotificationListener listener,
1281                final ComponentName component, final int userid) {
1282            enforceSystemOrSystemUI("INotificationManager.registerListener");
1283            mListeners.registerService(listener, component, userid);
1284        }
1285
1286        /**
1287         * Remove a listener binder directly
1288         */
1289        @Override
1290        public void unregisterListener(INotificationListener listener, int userid) {
1291            mListeners.unregisterService(listener, userid);
1292        }
1293
1294        /**
1295         * Allow an INotificationListener to simulate a "clear all" operation.
1296         *
1297         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1298         *
1299         * @param token The binder for the listener, to check that the caller is allowed
1300         */
1301        @Override
1302        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1303            final int callingUid = Binder.getCallingUid();
1304            final int callingPid = Binder.getCallingPid();
1305            long identity = Binder.clearCallingIdentity();
1306            try {
1307                synchronized (mNotificationList) {
1308                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1309                    if (keys != null) {
1310                        final int N = keys.length;
1311                        for (int i = 0; i < N; i++) {
1312                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1313                            if (r == null) continue;
1314                            final int userId = r.sbn.getUserId();
1315                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1316                                    !mUserProfiles.isCurrentProfile(userId)) {
1317                                throw new SecurityException("Disallowed call from listener: "
1318                                        + info.service);
1319                            }
1320                            cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1321                                    r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1322                                    userId);
1323                        }
1324                    } else {
1325                        cancelAllLocked(callingUid, callingPid, info.userid,
1326                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1327                    }
1328                }
1329            } finally {
1330                Binder.restoreCallingIdentity(identity);
1331            }
1332        }
1333
1334        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1335                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1336            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1337                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1338                    true,
1339                    userId, REASON_LISTENER_CANCEL, info);
1340        }
1341
1342        /**
1343         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1344         *
1345         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1346         *
1347         * @param token The binder for the listener, to check that the caller is allowed
1348         */
1349        @Override
1350        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1351                String tag, int id) {
1352            final int callingUid = Binder.getCallingUid();
1353            final int callingPid = Binder.getCallingPid();
1354            long identity = Binder.clearCallingIdentity();
1355            try {
1356                synchronized (mNotificationList) {
1357                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1358                    if (info.supportsProfiles()) {
1359                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1360                                + "from " + info.component
1361                                + " use cancelNotification(key) instead.");
1362                    } else {
1363                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1364                                pkg, tag, id, info.userid);
1365                    }
1366                }
1367            } finally {
1368                Binder.restoreCallingIdentity(identity);
1369            }
1370        }
1371
1372        /**
1373         * Allow an INotificationListener to request the list of outstanding notifications seen by
1374         * the current user. Useful when starting up, after which point the listener callbacks
1375         * should be used.
1376         *
1377         * @param token The binder for the listener, to check that the caller is allowed
1378         * @param keys An array of notification keys to fetch, or null to fetch everything
1379         * @returns The return value will contain the notifications specified in keys, in that
1380         *      order, or if keys is null, all the notifications, in natural order.
1381         */
1382        @Override
1383        public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1384                INotificationListener token, String[] keys, int trim) {
1385            synchronized (mNotificationList) {
1386                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1387                final boolean getKeys = keys != null;
1388                final int N = getKeys ? keys.length : mNotificationList.size();
1389                final ArrayList<StatusBarNotification> list
1390                        = new ArrayList<StatusBarNotification>(N);
1391                for (int i=0; i<N; i++) {
1392                    final NotificationRecord r = getKeys
1393                            ? mNotificationsByKey.get(keys[i])
1394                            : mNotificationList.get(i);
1395                    if (r == null) continue;
1396                    StatusBarNotification sbn = r.sbn;
1397                    if (!isVisibleToListener(sbn, info)) continue;
1398                    StatusBarNotification sbnToSend =
1399                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1400                    list.add(sbnToSend);
1401                }
1402                return new ParceledListSlice<StatusBarNotification>(list);
1403            }
1404        }
1405
1406        @Override
1407        public void requestHintsFromListener(INotificationListener token, int hints) {
1408            final long identity = Binder.clearCallingIdentity();
1409            try {
1410                synchronized (mNotificationList) {
1411                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1412                    final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1413                    if (disableEffects) {
1414                        mListenersDisablingEffects.add(info);
1415                    } else {
1416                        mListenersDisablingEffects.remove(info);
1417                    }
1418                    updateListenerHintsLocked();
1419                    updateEffectsSuppressorLocked();
1420                }
1421            } finally {
1422                Binder.restoreCallingIdentity(identity);
1423            }
1424        }
1425
1426        @Override
1427        public int getHintsFromListener(INotificationListener token) {
1428            synchronized (mNotificationList) {
1429                return mListenerHints;
1430            }
1431        }
1432
1433        @Override
1434        public void requestInterruptionFilterFromListener(INotificationListener token,
1435                int interruptionFilter) throws RemoteException {
1436            final long identity = Binder.clearCallingIdentity();
1437            try {
1438                synchronized (mNotificationList) {
1439                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1440                    mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1441                    updateInterruptionFilterLocked();
1442                }
1443            } finally {
1444                Binder.restoreCallingIdentity(identity);
1445            }
1446        }
1447
1448        @Override
1449        public int getInterruptionFilterFromListener(INotificationListener token)
1450                throws RemoteException {
1451            synchronized (mNotificationLight) {
1452                return mInterruptionFilter;
1453            }
1454        }
1455
1456        @Override
1457        public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1458                throws RemoteException {
1459            synchronized (mNotificationList) {
1460                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1461                if (info == null) return;
1462                mListeners.setOnNotificationPostedTrimLocked(info, trim);
1463            }
1464        }
1465
1466        @Override
1467        public ZenModeConfig getZenModeConfig() {
1468            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
1469            return mZenModeHelper.getConfig();
1470        }
1471
1472        @Override
1473        public boolean setZenModeConfig(ZenModeConfig config) {
1474            checkCallerIsSystem();
1475            return mZenModeHelper.setConfig(config);
1476        }
1477
1478        @Override
1479        public void notifyConditions(String pkg, IConditionProvider provider,
1480                Condition[] conditions) {
1481            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1482            checkCallerIsSystemOrSameApp(pkg);
1483            final long identity = Binder.clearCallingIdentity();
1484            try {
1485                mConditionProviders.notifyConditions(pkg, info, conditions);
1486            } finally {
1487                Binder.restoreCallingIdentity(identity);
1488            }
1489        }
1490
1491        @Override
1492        public void requestZenModeConditions(IConditionListener callback, int relevance) {
1493            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
1494            mConditionProviders.requestZenModeConditions(callback, relevance);
1495        }
1496
1497        @Override
1498        public void setZenModeCondition(Condition condition) {
1499            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
1500            final long identity = Binder.clearCallingIdentity();
1501            try {
1502                mConditionProviders.setZenModeCondition(condition, "binderCall");
1503            } finally {
1504                Binder.restoreCallingIdentity(identity);
1505            }
1506        }
1507
1508        @Override
1509        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1510            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1511            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1512        }
1513
1514        @Override
1515        public Condition[] getAutomaticZenModeConditions() {
1516            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1517            return mConditionProviders.getAutomaticZenModeConditions();
1518        }
1519
1520        private void enforceSystemOrSystemUI(String message) {
1521            if (isCallerSystem()) return;
1522            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1523                    message);
1524        }
1525
1526        @Override
1527        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1528            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1529                    != PackageManager.PERMISSION_GRANTED) {
1530                pw.println("Permission Denial: can't dump NotificationManager from pid="
1531                        + Binder.getCallingPid()
1532                        + ", uid=" + Binder.getCallingUid());
1533                return;
1534            }
1535
1536            dumpImpl(pw, DumpFilter.parseFromArguments(args));
1537        }
1538
1539        @Override
1540        public ComponentName getEffectsSuppressor() {
1541            enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
1542            return mEffectsSuppressor;
1543        }
1544
1545        @Override
1546        public boolean matchesCallFilter(Bundle extras) {
1547            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
1548            return mZenModeHelper.matchesCallFilter(
1549                    UserHandle.getCallingUserHandle(),
1550                    extras,
1551                    mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1552                    MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1553                    MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
1554        }
1555
1556        @Override
1557        public boolean isSystemConditionProviderEnabled(String path) {
1558            enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
1559            return mConditionProviders.isSystemConditionProviderEnabled(path);
1560        }
1561    };
1562
1563    private String[] getActiveNotificationKeys(INotificationListener token) {
1564        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1565        final ArrayList<String> keys = new ArrayList<String>();
1566        if (info.isEnabledForCurrentProfiles()) {
1567            synchronized (mNotificationList) {
1568                final int N = mNotificationList.size();
1569                for (int i = 0; i < N; i++) {
1570                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1571                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1572                        keys.add(sbn.getKey());
1573                    }
1574                }
1575            }
1576        }
1577        return keys.toArray(new String[keys.size()]);
1578    }
1579
1580    private String disableNotificationEffects(NotificationRecord record) {
1581        if (mDisableNotificationEffects) {
1582            return "booleanState";
1583        }
1584        if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1585            return "listenerHints";
1586        }
1587        if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1588            return "callState";
1589        }
1590        return null;
1591    }
1592
1593    void dumpImpl(PrintWriter pw, DumpFilter filter) {
1594        pw.print("Current Notification Manager state");
1595        if (filter != null) {
1596            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
1597        }
1598        pw.println(':');
1599        int N;
1600        final boolean zenOnly = filter != null && filter.zen;
1601
1602        if (!zenOnly) {
1603            synchronized (mToastQueue) {
1604                N = mToastQueue.size();
1605                if (N > 0) {
1606                    pw.println("  Toast Queue:");
1607                    for (int i=0; i<N; i++) {
1608                        mToastQueue.get(i).dump(pw, "    ", filter);
1609                    }
1610                    pw.println("  ");
1611                }
1612            }
1613        }
1614
1615        synchronized (mNotificationList) {
1616            if (!zenOnly) {
1617                N = mNotificationList.size();
1618                if (N > 0) {
1619                    pw.println("  Notification List:");
1620                    for (int i=0; i<N; i++) {
1621                        final NotificationRecord nr = mNotificationList.get(i);
1622                        if (filter != null && !filter.matches(nr.sbn)) continue;
1623                        nr.dump(pw, "    ", getContext());
1624                    }
1625                    pw.println("  ");
1626                }
1627
1628                if (filter == null) {
1629                    N = mLights.size();
1630                    if (N > 0) {
1631                        pw.println("  Lights List:");
1632                        for (int i=0; i<N; i++) {
1633                            if (i == N - 1) {
1634                                pw.print("  > ");
1635                            } else {
1636                                pw.print("    ");
1637                            }
1638                            pw.println(mLights.get(i));
1639                        }
1640                        pw.println("  ");
1641                    }
1642                    pw.println("  mUseAttentionLight=" + mUseAttentionLight);
1643                    pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
1644                    pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
1645                    pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
1646                    pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
1647                    pw.println("  mCallState=" + callStateToString(mCallState));
1648                    pw.println("  mSystemReady=" + mSystemReady);
1649                }
1650                pw.println("  mArchive=" + mArchive.toString());
1651                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1652                int i=0;
1653                while (iter.hasNext()) {
1654                    final StatusBarNotification sbn = iter.next();
1655                    if (filter != null && !filter.matches(sbn)) continue;
1656                    pw.println("    " + sbn);
1657                    if (++i >= 5) {
1658                        if (iter.hasNext()) pw.println("    ...");
1659                        break;
1660                    }
1661                }
1662            }
1663
1664            if (!zenOnly) {
1665                pw.println("\n  Usage Stats:");
1666                mUsageStats.dump(pw, "    ", filter);
1667            }
1668
1669            if (filter == null || zenOnly) {
1670                pw.println("\n  Zen Mode:");
1671                mZenModeHelper.dump(pw, "    ");
1672
1673                pw.println("\n  Zen Log:");
1674                ZenLog.dump(pw, "    ");
1675            }
1676
1677            if (!zenOnly) {
1678                pw.println("\n  Ranking Config:");
1679                mRankingHelper.dump(pw, "    ", filter);
1680
1681                pw.println("\n  Notification listeners:");
1682                mListeners.dump(pw, filter);
1683                pw.print("    mListenerHints: "); pw.println(mListenerHints);
1684                pw.print("    mListenersDisablingEffects: (");
1685                N = mListenersDisablingEffects.size();
1686                for (int i = 0; i < N; i++) {
1687                    final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
1688                    if (i > 0) pw.print(',');
1689                    pw.print(listener.component);
1690                }
1691                pw.println(')');
1692            }
1693
1694            pw.println("\n  Condition providers:");
1695            mConditionProviders.dump(pw, filter);
1696
1697            pw.println("\n  Group summaries:");
1698            for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
1699                NotificationRecord r = entry.getValue();
1700                pw.println("    " + entry.getKey() + " -> " + r.getKey());
1701                if (mNotificationsByKey.get(r.getKey()) != r) {
1702                    pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
1703                    r.dump(pw, "      ", getContext());
1704                }
1705            }
1706        }
1707    }
1708
1709    /**
1710     * The private API only accessible to the system process.
1711     */
1712    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1713        @Override
1714        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1715                String tag, int id, Notification notification, int[] idReceived, int userId) {
1716            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1717                    idReceived, userId);
1718        }
1719
1720        @Override
1721        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
1722                int userId) {
1723            checkCallerIsSystem();
1724            synchronized (mNotificationList) {
1725                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
1726                if (i < 0) {
1727                    Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
1728                            + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
1729                    return;
1730                }
1731                NotificationRecord r = mNotificationList.get(i);
1732                StatusBarNotification sbn = r.sbn;
1733                // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
1734                // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
1735                // we have to revert to the flags we received initially *and* force remove
1736                // FLAG_FOREGROUND_SERVICE.
1737                sbn.getNotification().flags =
1738                        (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
1739                mRankingHelper.sort(mNotificationList);
1740                mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
1741            }
1742        }
1743    };
1744
1745    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1746            final int callingPid, final String tag, final int id, final Notification notification,
1747            int[] idOut, int incomingUserId) {
1748        if (DBG) {
1749            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1750                    + " notification=" + notification);
1751        }
1752        checkCallerIsSystemOrSameApp(pkg);
1753        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1754        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
1755
1756        final int userId = ActivityManager.handleIncomingUser(callingPid,
1757                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1758        final UserHandle user = new UserHandle(userId);
1759
1760        // Limit the number of notifications that any given package except the android
1761        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
1762        if (!isSystemNotification && !isNotificationFromListener) {
1763            synchronized (mNotificationList) {
1764                int count = 0;
1765                final int N = mNotificationList.size();
1766                for (int i=0; i<N; i++) {
1767                    final NotificationRecord r = mNotificationList.get(i);
1768                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1769                        count++;
1770                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1771                            Slog.e(TAG, "Package has already posted " + count
1772                                    + " notifications.  Not showing more.  package=" + pkg);
1773                            return;
1774                        }
1775                    }
1776                }
1777            }
1778        }
1779
1780        if (pkg == null || notification == null) {
1781            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1782                    + " id=" + id + " notification=" + notification);
1783        }
1784        if (notification.icon != 0) {
1785            if (!notification.isValid()) {
1786                throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
1787                        + " id=" + id + " notification=" + notification);
1788            }
1789        }
1790
1791        mHandler.post(new Runnable() {
1792            @Override
1793            public void run() {
1794
1795                synchronized (mNotificationList) {
1796
1797                    // === Scoring ===
1798
1799                    // 0. Sanitize inputs
1800                    notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1801                            Notification.PRIORITY_MAX);
1802                    // Migrate notification flags to scores
1803                    if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1804                        if (notification.priority < Notification.PRIORITY_MAX) {
1805                            notification.priority = Notification.PRIORITY_MAX;
1806                        }
1807                    } else if (SCORE_ONGOING_HIGHER &&
1808                            0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1809                        if (notification.priority < Notification.PRIORITY_HIGH) {
1810                            notification.priority = Notification.PRIORITY_HIGH;
1811                        }
1812                    }
1813
1814                    // 1. initial score: buckets of 10, around the app [-20..20]
1815                    final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
1816
1817                    // 2. extract ranking signals from the notification data
1818                    final StatusBarNotification n = new StatusBarNotification(
1819                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1820                            user);
1821                    NotificationRecord r = new NotificationRecord(n, score);
1822                    NotificationRecord old = mNotificationsByKey.get(n.getKey());
1823                    if (old != null) {
1824                        // Retain ranking information from previous record
1825                        r.copyRankingInformation(old);
1826                    }
1827
1828                    // Handle grouped notifications and bail out early if we
1829                    // can to avoid extracting signals.
1830                    handleGroupedNotificationLocked(r, old, callingUid, callingPid);
1831                    boolean ignoreNotification =
1832                            removeUnusedGroupedNotificationLocked(r, callingUid, callingPid);
1833
1834                    // This conditional is a dirty hack to limit the logging done on
1835                    //     behalf of the download manager without affecting other apps.
1836                    if (!pkg.equals("com.android.providers.downloads")
1837                            || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1838                        int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
1839                        if (ignoreNotification) {
1840                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
1841                        } else if (old != null) {
1842                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
1843                        }
1844                        EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1845                                pkg, id, tag, userId, notification.toString(),
1846                                enqueueStatus);
1847                    }
1848
1849                    if (ignoreNotification) {
1850                        return;
1851                    }
1852
1853                    mRankingHelper.extractSignals(r);
1854
1855                    // 3. Apply local rules
1856
1857                    // blocked apps
1858                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1859                        if (!isSystemNotification) {
1860                            r.score = JUNK_SCORE;
1861                            Slog.e(TAG, "Suppressing notification from package " + pkg
1862                                    + " by user request.");
1863                        }
1864                    }
1865
1866                    if (r.score < SCORE_DISPLAY_THRESHOLD) {
1867                        // Notification will be blocked because the score is too low.
1868                        return;
1869                    }
1870
1871                    int index = indexOfNotificationLocked(n.getKey());
1872                    if (index < 0) {
1873                        mNotificationList.add(r);
1874                        mUsageStats.registerPostedByApp(r);
1875                    } else {
1876                        old = mNotificationList.get(index);
1877                        mNotificationList.set(index, r);
1878                        mUsageStats.registerUpdatedByApp(r, old);
1879                        // Make sure we don't lose the foreground service state.
1880                        notification.flags |=
1881                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1882                        r.isUpdate = true;
1883                    }
1884
1885                    mNotificationsByKey.put(n.getKey(), r);
1886
1887                    // Ensure if this is a foreground service that the proper additional
1888                    // flags are set.
1889                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1890                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1891                                | Notification.FLAG_NO_CLEAR;
1892                    }
1893
1894                    applyZenModeLocked(r);
1895                    mRankingHelper.sort(mNotificationList);
1896
1897                    if (notification.icon != 0) {
1898                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
1899                        mListeners.notifyPostedLocked(n, oldSbn);
1900                    } else {
1901                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1902                        if (old != null && !old.isCanceled) {
1903                            mListeners.notifyRemovedLocked(n);
1904                        }
1905                        // ATTENTION: in a future release we will bail out here
1906                        // so that we do not play sounds, show lights, etc. for invalid
1907                        // notifications
1908                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1909                                + n.getPackageName());
1910                    }
1911
1912                    buzzBeepBlinkLocked(r);
1913                }
1914            }
1915        });
1916
1917        idOut[0] = id;
1918    }
1919
1920    /**
1921     * Ensures that grouped notification receive their special treatment.
1922     *
1923     * <p>Cancels group children if the new notification causes a group to lose
1924     * its summary.</p>
1925     *
1926     * <p>Updates mSummaryByGroupKey.</p>
1927     */
1928    private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
1929            int callingUid, int callingPid) {
1930        StatusBarNotification sbn = r.sbn;
1931        Notification n = sbn.getNotification();
1932        String group = sbn.getGroupKey();
1933        boolean isSummary = n.isGroupSummary();
1934
1935        Notification oldN = old != null ? old.sbn.getNotification() : null;
1936        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
1937        boolean oldIsSummary = old != null && oldN.isGroupSummary();
1938
1939        if (oldIsSummary) {
1940            NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
1941            if (removedSummary != old) {
1942                String removedKey =
1943                        removedSummary != null ? removedSummary.getKey() : "<null>";
1944                Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
1945                        ", removed=" + removedKey);
1946            }
1947        }
1948        if (isSummary) {
1949            mSummaryByGroupKey.put(group, r);
1950        }
1951
1952        // Clear out group children of the old notification if the update
1953        // causes the group summary to go away. This happens when the old
1954        // notification was a summary and the new one isn't, or when the old
1955        // notification was a summary and its group key changed.
1956        if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
1957            cancelGroupChildrenLocked(old, callingUid, callingPid, null,
1958                    REASON_GROUP_SUMMARY_CANCELED);
1959        }
1960    }
1961
1962    /**
1963     * Performs group notification optimizations if SysUI is the only active
1964     * notification listener and returns whether the given notification should
1965     * be ignored.
1966     *
1967     * <p>Returns true if the given notification is a child of a group with a
1968     * summary, which means that SysUI will never show it, and hence the new
1969     * notification can be safely ignored.</p>
1970     *
1971     * <p>For summaries, cancels all children of that group, as SysUI will
1972     * never show them anymore.</p>
1973     *
1974     * @return true if the given notification can be ignored as an optimization
1975     */
1976    private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
1977            int callingUid, int callingPid) {
1978        // No optimizations are possible if listeners want groups.
1979        if (mListeners.notificationGroupsDesired()) {
1980            return false;
1981        }
1982
1983        StatusBarNotification sbn = r.sbn;
1984        String group = sbn.getGroupKey();
1985        boolean isSummary = sbn.getNotification().isGroupSummary();
1986        boolean isChild = sbn.getNotification().isGroupChild();
1987
1988        NotificationRecord summary = mSummaryByGroupKey.get(group);
1989        if (isChild && summary != null) {
1990            // Child with an active summary -> ignore
1991            if (DBG) {
1992                Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
1993                        + summary.getKey());
1994            }
1995            return true;
1996        } else if (isSummary) {
1997            // Summary -> cancel children
1998            cancelGroupChildrenLocked(r, callingUid, callingPid, null,
1999                    REASON_GROUP_OPTIMIZATION);
2000        }
2001        return false;
2002    }
2003
2004    private void buzzBeepBlinkLocked(NotificationRecord record) {
2005        boolean buzzBeepBlinked = false;
2006        final Notification notification = record.sbn.getNotification();
2007
2008        // Should this notification make noise, vibe, or use the LED?
2009        final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
2010        final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2011        if (DBG || record.isIntercepted())
2012            Slog.v(TAG,
2013                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2014                            " intercept=" + record.isIntercepted()
2015            );
2016
2017        final int currentUser;
2018        final long token = Binder.clearCallingIdentity();
2019        try {
2020            currentUser = ActivityManager.getCurrentUser();
2021        } finally {
2022            Binder.restoreCallingIdentity(token);
2023        }
2024
2025        // If we're not supposed to beep, vibrate, etc. then don't.
2026        final String disableEffects = disableNotificationEffects(record);
2027        if (disableEffects != null) {
2028            ZenLog.traceDisableEffects(record, disableEffects);
2029        }
2030        if (disableEffects == null
2031                && (!(record.isUpdate
2032                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2033                && (record.getUserId() == UserHandle.USER_ALL ||
2034                    record.getUserId() == currentUser ||
2035                    mUserProfiles.isCurrentProfile(record.getUserId()))
2036                && canInterrupt
2037                && mSystemReady
2038                && mAudioManager != null) {
2039            if (DBG) Slog.v(TAG, "Interrupting!");
2040
2041            sendAccessibilityEvent(notification, record.sbn.getPackageName());
2042
2043            // sound
2044
2045            // should we use the default notification sound? (indicated either by
2046            // DEFAULT_SOUND or because notification.sound is pointing at
2047            // Settings.System.NOTIFICATION_SOUND)
2048            final boolean useDefaultSound =
2049                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2050                           Settings.System.DEFAULT_NOTIFICATION_URI
2051                                   .equals(notification.sound);
2052
2053            Uri soundUri = null;
2054            boolean hasValidSound = false;
2055
2056            if (useDefaultSound) {
2057                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2058
2059                // check to see if the default notification sound is silent
2060                ContentResolver resolver = getContext().getContentResolver();
2061                hasValidSound = Settings.System.getString(resolver,
2062                       Settings.System.NOTIFICATION_SOUND) != null;
2063            } else if (notification.sound != null) {
2064                soundUri = notification.sound;
2065                hasValidSound = (soundUri != null);
2066            }
2067
2068            if (hasValidSound) {
2069                boolean looping =
2070                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
2071                AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2072                mSoundNotificationKey = record.getKey();
2073                // do not play notifications if stream volume is 0 (typically because
2074                // ringer mode is silent) or if there is a user of exclusive audio focus
2075                if ((mAudioManager.getStreamVolume(
2076                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2077                            && !mAudioManager.isAudioFocusExclusive()) {
2078                    final long identity = Binder.clearCallingIdentity();
2079                    try {
2080                        final IRingtonePlayer player =
2081                                mAudioManager.getRingtonePlayer();
2082                        if (player != null) {
2083                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2084                                    + " with attributes " + audioAttributes);
2085                            player.playAsync(soundUri, record.sbn.getUser(), looping,
2086                                    audioAttributes);
2087                            buzzBeepBlinked = true;
2088                        }
2089                    } catch (RemoteException e) {
2090                    } finally {
2091                        Binder.restoreCallingIdentity(identity);
2092                    }
2093                }
2094            }
2095
2096            // vibrate
2097            // Does the notification want to specify its own vibration?
2098            final boolean hasCustomVibrate = notification.vibrate != null;
2099
2100            // new in 4.2: if there was supposed to be a sound and we're in vibrate
2101            // mode, and no other vibration is specified, we fall back to vibration
2102            final boolean convertSoundToVibration =
2103                       !hasCustomVibrate
2104                    && hasValidSound
2105                    && (mAudioManager.getRingerModeInternal()
2106                               == AudioManager.RINGER_MODE_VIBRATE);
2107
2108            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2109            final boolean useDefaultVibrate =
2110                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2111
2112            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2113                    && !(mAudioManager.getRingerModeInternal()
2114                            == AudioManager.RINGER_MODE_SILENT)) {
2115                mVibrateNotificationKey = record.getKey();
2116
2117                if (useDefaultVibrate || convertSoundToVibration) {
2118                    // Escalate privileges so we can use the vibrator even if the
2119                    // notifying app does not have the VIBRATE permission.
2120                    long identity = Binder.clearCallingIdentity();
2121                    try {
2122                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2123                            useDefaultVibrate ? mDefaultVibrationPattern
2124                                : mFallbackVibrationPattern,
2125                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2126                                ? 0: -1, audioAttributesForNotification(notification));
2127                        buzzBeepBlinked = true;
2128                    } finally {
2129                        Binder.restoreCallingIdentity(identity);
2130                    }
2131                } else if (notification.vibrate.length > 1) {
2132                    // If you want your own vibration pattern, you need the VIBRATE
2133                    // permission
2134                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2135                            notification.vibrate,
2136                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2137                                ? 0: -1, audioAttributesForNotification(notification));
2138                    buzzBeepBlinked = true;
2139                }
2140            }
2141        }
2142
2143        // light
2144        // release the light
2145        boolean wasShowLights = mLights.remove(record.getKey());
2146        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
2147            mLights.add(record.getKey());
2148            updateLightsLocked();
2149            if (mUseAttentionLight) {
2150                mAttentionLight.pulse();
2151            }
2152            buzzBeepBlinked = true;
2153        } else if (wasShowLights) {
2154            updateLightsLocked();
2155        }
2156        if (buzzBeepBlinked) {
2157            mHandler.post(mBuzzBeepBlinked);
2158        }
2159    }
2160
2161    private static AudioAttributes audioAttributesForNotification(Notification n) {
2162        if (n.audioAttributes != null
2163                && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2164            // the audio attributes are set and different from the default, use them
2165            return n.audioAttributes;
2166        } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2167            // the stream type is valid, use it
2168            return new AudioAttributes.Builder()
2169                    .setInternalLegacyStreamType(n.audioStreamType)
2170                    .build();
2171        } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2172            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2173        } else {
2174            Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2175            return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2176        }
2177    }
2178
2179    void showNextToastLocked() {
2180        ToastRecord record = mToastQueue.get(0);
2181        while (record != null) {
2182            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2183            try {
2184                record.callback.show();
2185                scheduleTimeoutLocked(record);
2186                return;
2187            } catch (RemoteException e) {
2188                Slog.w(TAG, "Object died trying to show notification " + record.callback
2189                        + " in package " + record.pkg);
2190                // remove it from the list and let the process die
2191                int index = mToastQueue.indexOf(record);
2192                if (index >= 0) {
2193                    mToastQueue.remove(index);
2194                }
2195                keepProcessAliveLocked(record.pid);
2196                if (mToastQueue.size() > 0) {
2197                    record = mToastQueue.get(0);
2198                } else {
2199                    record = null;
2200                }
2201            }
2202        }
2203    }
2204
2205    void cancelToastLocked(int index) {
2206        ToastRecord record = mToastQueue.get(index);
2207        try {
2208            record.callback.hide();
2209        } catch (RemoteException e) {
2210            Slog.w(TAG, "Object died trying to hide notification " + record.callback
2211                    + " in package " + record.pkg);
2212            // don't worry about this, we're about to remove it from
2213            // the list anyway
2214        }
2215        mToastQueue.remove(index);
2216        keepProcessAliveLocked(record.pid);
2217        if (mToastQueue.size() > 0) {
2218            // Show the next one. If the callback fails, this will remove
2219            // it from the list, so don't assume that the list hasn't changed
2220            // after this point.
2221            showNextToastLocked();
2222        }
2223    }
2224
2225    private void scheduleTimeoutLocked(ToastRecord r)
2226    {
2227        mHandler.removeCallbacksAndMessages(r);
2228        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2229        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2230        mHandler.sendMessageDelayed(m, delay);
2231    }
2232
2233    private void handleTimeout(ToastRecord record)
2234    {
2235        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2236        synchronized (mToastQueue) {
2237            int index = indexOfToastLocked(record.pkg, record.callback);
2238            if (index >= 0) {
2239                cancelToastLocked(index);
2240            }
2241        }
2242    }
2243
2244    // lock on mToastQueue
2245    int indexOfToastLocked(String pkg, ITransientNotification callback)
2246    {
2247        IBinder cbak = callback.asBinder();
2248        ArrayList<ToastRecord> list = mToastQueue;
2249        int len = list.size();
2250        for (int i=0; i<len; i++) {
2251            ToastRecord r = list.get(i);
2252            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2253                return i;
2254            }
2255        }
2256        return -1;
2257    }
2258
2259    // lock on mToastQueue
2260    void keepProcessAliveLocked(int pid)
2261    {
2262        int toastCount = 0; // toasts from this pid
2263        ArrayList<ToastRecord> list = mToastQueue;
2264        int N = list.size();
2265        for (int i=0; i<N; i++) {
2266            ToastRecord r = list.get(i);
2267            if (r.pid == pid) {
2268                toastCount++;
2269            }
2270        }
2271        try {
2272            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2273        } catch (RemoteException e) {
2274            // Shouldn't happen.
2275        }
2276    }
2277
2278    private void handleRankingReconsideration(Message message) {
2279        if (!(message.obj instanceof RankingReconsideration)) return;
2280        RankingReconsideration recon = (RankingReconsideration) message.obj;
2281        recon.run();
2282        boolean changed;
2283        synchronized (mNotificationList) {
2284            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2285            if (record == null) {
2286                return;
2287            }
2288            int indexBefore = findNotificationRecordIndexLocked(record);
2289            boolean interceptBefore = record.isIntercepted();
2290            int visibilityBefore = record.getPackageVisibilityOverride();
2291            recon.applyChangesLocked(record);
2292            applyZenModeLocked(record);
2293            mRankingHelper.sort(mNotificationList);
2294            int indexAfter = findNotificationRecordIndexLocked(record);
2295            boolean interceptAfter = record.isIntercepted();
2296            int visibilityAfter = record.getPackageVisibilityOverride();
2297            changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2298                    || visibilityBefore != visibilityAfter;
2299            if (interceptBefore && !interceptAfter) {
2300                buzzBeepBlinkLocked(record);
2301            }
2302        }
2303        if (changed) {
2304            scheduleSendRankingUpdate();
2305        }
2306    }
2307
2308    private void handleRankingConfigChange() {
2309        synchronized (mNotificationList) {
2310            final int N = mNotificationList.size();
2311            ArrayList<String> orderBefore = new ArrayList<String>(N);
2312            int[] visibilities = new int[N];
2313            for (int i = 0; i < N; i++) {
2314                final NotificationRecord r = mNotificationList.get(i);
2315                orderBefore.add(r.getKey());
2316                visibilities[i] = r.getPackageVisibilityOverride();
2317                mRankingHelper.extractSignals(r);
2318            }
2319            for (int i = 0; i < N; i++) {
2320                mRankingHelper.sort(mNotificationList);
2321                final NotificationRecord r = mNotificationList.get(i);
2322                if (!orderBefore.get(i).equals(r.getKey())
2323                        || visibilities[i] != r.getPackageVisibilityOverride()) {
2324                    scheduleSendRankingUpdate();
2325                    return;
2326                }
2327            }
2328        }
2329    }
2330
2331    // let zen mode evaluate this record
2332    private void applyZenModeLocked(NotificationRecord record) {
2333        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2334    }
2335
2336    // lock on mNotificationList
2337    private int findNotificationRecordIndexLocked(NotificationRecord target) {
2338        return mRankingHelper.indexOf(mNotificationList, target);
2339    }
2340
2341    private void scheduleSendRankingUpdate() {
2342        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2343        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2344        mHandler.sendMessage(m);
2345    }
2346
2347    private void handleSendRankingUpdate() {
2348        synchronized (mNotificationList) {
2349            mListeners.notifyRankingUpdateLocked();
2350        }
2351    }
2352
2353    private void scheduleListenerHintsChanged(int state) {
2354        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2355        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2356    }
2357
2358    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2359        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2360        mHandler.obtainMessage(
2361                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2362                listenerInterruptionFilter,
2363                0).sendToTarget();
2364    }
2365
2366    private void handleListenerHintsChanged(int hints) {
2367        synchronized (mNotificationList) {
2368            mListeners.notifyListenerHintsChangedLocked(hints);
2369        }
2370    }
2371
2372    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2373        synchronized (mNotificationList) {
2374            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2375        }
2376    }
2377
2378    private final class WorkerHandler extends Handler
2379    {
2380        @Override
2381        public void handleMessage(Message msg)
2382        {
2383            switch (msg.what)
2384            {
2385                case MESSAGE_TIMEOUT:
2386                    handleTimeout((ToastRecord)msg.obj);
2387                    break;
2388                case MESSAGE_SAVE_POLICY_FILE:
2389                    handleSavePolicyFile();
2390                    break;
2391                case MESSAGE_SEND_RANKING_UPDATE:
2392                    handleSendRankingUpdate();
2393                    break;
2394                case MESSAGE_LISTENER_HINTS_CHANGED:
2395                    handleListenerHintsChanged(msg.arg1);
2396                    break;
2397                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2398                    handleListenerInterruptionFilterChanged(msg.arg1);
2399                    break;
2400            }
2401        }
2402
2403    }
2404
2405    private final class RankingWorkerHandler extends Handler
2406    {
2407        public RankingWorkerHandler(Looper looper) {
2408            super(looper);
2409        }
2410
2411        @Override
2412        public void handleMessage(Message msg) {
2413            switch (msg.what) {
2414                case MESSAGE_RECONSIDER_RANKING:
2415                    handleRankingReconsideration(msg);
2416                    break;
2417                case MESSAGE_RANKING_CONFIG_CHANGE:
2418                    handleRankingConfigChange();
2419                    break;
2420            }
2421        }
2422    }
2423
2424    // Notifications
2425    // ============================================================================
2426    static int clamp(int x, int low, int high) {
2427        return (x < low) ? low : ((x > high) ? high : x);
2428    }
2429
2430    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2431        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2432        if (!manager.isEnabled()) {
2433            return;
2434        }
2435
2436        AccessibilityEvent event =
2437            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2438        event.setPackageName(packageName);
2439        event.setClassName(Notification.class.getName());
2440        event.setParcelableData(notification);
2441        CharSequence tickerText = notification.tickerText;
2442        if (!TextUtils.isEmpty(tickerText)) {
2443            event.getText().add(tickerText);
2444        }
2445
2446        manager.sendAccessibilityEvent(event);
2447    }
2448
2449    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2450        // tell the app
2451        if (sendDelete) {
2452            if (r.getNotification().deleteIntent != null) {
2453                try {
2454                    r.getNotification().deleteIntent.send();
2455                } catch (PendingIntent.CanceledException ex) {
2456                    // do nothing - there's no relevant way to recover, and
2457                    //     no reason to let this propagate
2458                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2459                }
2460            }
2461        }
2462
2463        // status bar
2464        if (r.getNotification().icon != 0) {
2465            r.isCanceled = true;
2466            mListeners.notifyRemovedLocked(r.sbn);
2467        }
2468
2469        final String canceledKey = r.getKey();
2470
2471        // sound
2472        if (canceledKey.equals(mSoundNotificationKey)) {
2473            mSoundNotificationKey = null;
2474            final long identity = Binder.clearCallingIdentity();
2475            try {
2476                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2477                if (player != null) {
2478                    player.stopAsync();
2479                }
2480            } catch (RemoteException e) {
2481            } finally {
2482                Binder.restoreCallingIdentity(identity);
2483            }
2484        }
2485
2486        // vibrate
2487        if (canceledKey.equals(mVibrateNotificationKey)) {
2488            mVibrateNotificationKey = null;
2489            long identity = Binder.clearCallingIdentity();
2490            try {
2491                mVibrator.cancel();
2492            }
2493            finally {
2494                Binder.restoreCallingIdentity(identity);
2495            }
2496        }
2497
2498        // light
2499        mLights.remove(canceledKey);
2500
2501        // Record usage stats
2502        switch (reason) {
2503            case REASON_DELEGATE_CANCEL:
2504            case REASON_DELEGATE_CANCEL_ALL:
2505            case REASON_LISTENER_CANCEL:
2506            case REASON_LISTENER_CANCEL_ALL:
2507                mUsageStats.registerDismissedByUser(r);
2508                break;
2509            case REASON_NOMAN_CANCEL:
2510            case REASON_NOMAN_CANCEL_ALL:
2511                mUsageStats.registerRemovedByApp(r);
2512                break;
2513            case REASON_DELEGATE_CLICK:
2514                mUsageStats.registerCancelDueToClick(r);
2515                break;
2516            default:
2517                mUsageStats.registerCancelUnknown(r);
2518                break;
2519        }
2520
2521        mNotificationsByKey.remove(r.sbn.getKey());
2522        String groupKey = r.getGroupKey();
2523        NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2524        if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2525            mSummaryByGroupKey.remove(groupKey);
2526        }
2527
2528        // Save it for users of getHistoricalNotifications()
2529        mArchive.record(r.sbn);
2530
2531        EventLogTags.writeNotificationCanceled(canceledKey, reason);
2532    }
2533
2534    /**
2535     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2536     * and none of the {@code mustNotHaveFlags}.
2537     */
2538    void cancelNotification(final int callingUid, final int callingPid,
2539            final String pkg, final String tag, final int id,
2540            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2541            final int userId, final int reason, final ManagedServiceInfo listener) {
2542        // In enqueueNotificationInternal notifications are added by scheduling the
2543        // work on the worker handler. Hence, we also schedule the cancel on this
2544        // handler to avoid a scenario where an add notification call followed by a
2545        // remove notification call ends up in not removing the notification.
2546        mHandler.post(new Runnable() {
2547            @Override
2548            public void run() {
2549                String listenerName = listener == null ? null : listener.component.toShortString();
2550                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2551                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2552
2553                synchronized (mNotificationList) {
2554                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2555                    if (index >= 0) {
2556                        NotificationRecord r = mNotificationList.get(index);
2557
2558                        // Ideally we'd do this in the caller of this method. However, that would
2559                        // require the caller to also find the notification.
2560                        if (reason == REASON_DELEGATE_CLICK) {
2561                            mUsageStats.registerClickedByUser(r);
2562                        }
2563
2564                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2565                            return;
2566                        }
2567                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2568                            return;
2569                        }
2570
2571                        mNotificationList.remove(index);
2572
2573                        cancelNotificationLocked(r, sendDelete, reason);
2574                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2575                                REASON_GROUP_SUMMARY_CANCELED);
2576                        updateLightsLocked();
2577                    }
2578                }
2579            }
2580        });
2581    }
2582
2583    /**
2584     * Determine whether the userId applies to the notification in question, either because
2585     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2586     */
2587    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2588        return
2589                // looking for USER_ALL notifications? match everything
2590                   userId == UserHandle.USER_ALL
2591                // a notification sent to USER_ALL matches any query
2592                || r.getUserId() == UserHandle.USER_ALL
2593                // an exact user match
2594                || r.getUserId() == userId;
2595    }
2596
2597    /**
2598     * Determine whether the userId applies to the notification in question, either because
2599     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2600     * because it matches one of the users profiles.
2601     */
2602    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2603        return notificationMatchesUserId(r, userId)
2604                || mUserProfiles.isCurrentProfile(r.getUserId());
2605    }
2606
2607    /**
2608     * Cancels all notifications from a given package that have all of the
2609     * {@code mustHaveFlags}.
2610     */
2611    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2612            int mustNotHaveFlags, boolean doit, int userId, int reason,
2613            ManagedServiceInfo listener) {
2614        String listenerName = listener == null ? null : listener.component.toShortString();
2615        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2616                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2617                listenerName);
2618
2619        synchronized (mNotificationList) {
2620            final int N = mNotificationList.size();
2621            ArrayList<NotificationRecord> canceledNotifications = null;
2622            for (int i = N-1; i >= 0; --i) {
2623                NotificationRecord r = mNotificationList.get(i);
2624                if (!notificationMatchesUserId(r, userId)) {
2625                    continue;
2626                }
2627                // Don't remove notifications to all, if there's no package name specified
2628                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2629                    continue;
2630                }
2631                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2632                    continue;
2633                }
2634                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2635                    continue;
2636                }
2637                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2638                    continue;
2639                }
2640                if (canceledNotifications == null) {
2641                    canceledNotifications = new ArrayList<>();
2642                }
2643                canceledNotifications.add(r);
2644                if (!doit) {
2645                    return true;
2646                }
2647                mNotificationList.remove(i);
2648                cancelNotificationLocked(r, false, reason);
2649            }
2650            if (doit && canceledNotifications != null) {
2651                final int M = canceledNotifications.size();
2652                for (int i = 0; i < M; i++) {
2653                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2654                            listenerName, REASON_GROUP_SUMMARY_CANCELED);
2655                }
2656            }
2657            if (canceledNotifications != null) {
2658                updateLightsLocked();
2659            }
2660            return canceledNotifications != null;
2661        }
2662    }
2663
2664    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2665            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2666        String listenerName = listener == null ? null : listener.component.toShortString();
2667        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2668                null, userId, 0, 0, reason, listenerName);
2669
2670        ArrayList<NotificationRecord> canceledNotifications = null;
2671        final int N = mNotificationList.size();
2672        for (int i=N-1; i>=0; i--) {
2673            NotificationRecord r = mNotificationList.get(i);
2674            if (includeCurrentProfiles) {
2675                if (!notificationMatchesCurrentProfiles(r, userId)) {
2676                    continue;
2677                }
2678            } else {
2679                if (!notificationMatchesUserId(r, userId)) {
2680                    continue;
2681                }
2682            }
2683
2684            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2685                            | Notification.FLAG_NO_CLEAR)) == 0) {
2686                mNotificationList.remove(i);
2687                cancelNotificationLocked(r, true, reason);
2688                // Make a note so we can cancel children later.
2689                if (canceledNotifications == null) {
2690                    canceledNotifications = new ArrayList<>();
2691                }
2692                canceledNotifications.add(r);
2693            }
2694        }
2695        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2696        for (int i = 0; i < M; i++) {
2697            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2698                    listenerName, REASON_GROUP_SUMMARY_CANCELED);
2699        }
2700        updateLightsLocked();
2701    }
2702
2703    // Warning: The caller is responsible for invoking updateLightsLocked().
2704    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2705            String listenerName, int reason) {
2706        Notification n = r.getNotification();
2707        if (!n.isGroupSummary()) {
2708            return;
2709        }
2710
2711        String pkg = r.sbn.getPackageName();
2712        int userId = r.getUserId();
2713
2714        if (pkg == null) {
2715            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2716            return;
2717        }
2718
2719        final int N = mNotificationList.size();
2720        for (int i = N - 1; i >= 0; i--) {
2721            NotificationRecord childR = mNotificationList.get(i);
2722            StatusBarNotification childSbn = childR.sbn;
2723            if (childR.getNotification().isGroupChild() &&
2724                    childR.getGroupKey().equals(r.getGroupKey())) {
2725                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
2726                        childSbn.getTag(), userId, 0, 0, reason, listenerName);
2727                mNotificationList.remove(i);
2728                cancelNotificationLocked(childR, false, reason);
2729            }
2730        }
2731    }
2732
2733    // lock on mNotificationList
2734    void updateLightsLocked()
2735    {
2736        // handle notification lights
2737        NotificationRecord ledNotification = null;
2738        while (ledNotification == null && !mLights.isEmpty()) {
2739            final String owner = mLights.get(mLights.size() - 1);
2740            ledNotification = mNotificationsByKey.get(owner);
2741            if (ledNotification == null) {
2742                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
2743                mLights.remove(owner);
2744            }
2745        }
2746
2747        // Don't flash while we are in a call or screen is on
2748        if (ledNotification == null || mInCall || mScreenOn) {
2749            mNotificationLight.turnOff();
2750            mStatusBar.notificationLightOff();
2751        } else {
2752            final Notification ledno = ledNotification.sbn.getNotification();
2753            int ledARGB = ledno.ledARGB;
2754            int ledOnMS = ledno.ledOnMS;
2755            int ledOffMS = ledno.ledOffMS;
2756            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2757                ledARGB = mDefaultNotificationColor;
2758                ledOnMS = mDefaultNotificationLedOn;
2759                ledOffMS = mDefaultNotificationLedOff;
2760            }
2761            if (mNotificationPulseEnabled) {
2762                // pulse repeatedly
2763                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2764                        ledOnMS, ledOffMS);
2765            }
2766            // let SystemUI make an independent decision
2767            mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
2768        }
2769    }
2770
2771    // lock on mNotificationList
2772    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2773    {
2774        ArrayList<NotificationRecord> list = mNotificationList;
2775        final int len = list.size();
2776        for (int i=0; i<len; i++) {
2777            NotificationRecord r = list.get(i);
2778            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2779                continue;
2780            }
2781            if (tag == null) {
2782                if (r.sbn.getTag() != null) {
2783                    continue;
2784                }
2785            } else {
2786                if (!tag.equals(r.sbn.getTag())) {
2787                    continue;
2788                }
2789            }
2790            if (r.sbn.getPackageName().equals(pkg)) {
2791                return i;
2792            }
2793        }
2794        return -1;
2795    }
2796
2797    // lock on mNotificationList
2798    int indexOfNotificationLocked(String key) {
2799        final int N = mNotificationList.size();
2800        for (int i = 0; i < N; i++) {
2801            if (key.equals(mNotificationList.get(i).getKey())) {
2802                return i;
2803            }
2804        }
2805        return -1;
2806    }
2807
2808    private void updateNotificationPulse() {
2809        synchronized (mNotificationList) {
2810            updateLightsLocked();
2811        }
2812    }
2813
2814    private static boolean isUidSystem(int uid) {
2815        final int appid = UserHandle.getAppId(uid);
2816        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2817    }
2818
2819    private static boolean isCallerSystem() {
2820        return isUidSystem(Binder.getCallingUid());
2821    }
2822
2823    private static void checkCallerIsSystem() {
2824        if (isCallerSystem()) {
2825            return;
2826        }
2827        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2828    }
2829
2830    private static void checkCallerIsSystemOrSameApp(String pkg) {
2831        if (isCallerSystem()) {
2832            return;
2833        }
2834        final int uid = Binder.getCallingUid();
2835        try {
2836            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2837                    pkg, 0, UserHandle.getCallingUserId());
2838            if (ai == null) {
2839                throw new SecurityException("Unknown package " + pkg);
2840            }
2841            if (!UserHandle.isSameApp(ai.uid, uid)) {
2842                throw new SecurityException("Calling uid " + uid + " gave package"
2843                        + pkg + " which is owned by uid " + ai.uid);
2844            }
2845        } catch (RemoteException re) {
2846            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2847        }
2848    }
2849
2850    private static String callStateToString(int state) {
2851        switch (state) {
2852            case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
2853            case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
2854            case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
2855            default: return "CALL_STATE_UNKNOWN_" + state;
2856        }
2857    }
2858
2859    private void listenForCallState() {
2860        TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
2861            @Override
2862            public void onCallStateChanged(int state, String incomingNumber) {
2863                if (mCallState == state) return;
2864                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
2865                mCallState = state;
2866            }
2867        }, PhoneStateListener.LISTEN_CALL_STATE);
2868    }
2869
2870    /**
2871     * Generates a NotificationRankingUpdate from 'sbns', considering only
2872     * notifications visible to the given listener.
2873     *
2874     * <p>Caller must hold a lock on mNotificationList.</p>
2875     */
2876    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2877        int speedBumpIndex = -1;
2878        final int N = mNotificationList.size();
2879        ArrayList<String> keys = new ArrayList<String>(N);
2880        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
2881        Bundle visibilityOverrides = new Bundle();
2882        for (int i = 0; i < N; i++) {
2883            NotificationRecord record = mNotificationList.get(i);
2884            if (!isVisibleToListener(record.sbn, info)) {
2885                continue;
2886            }
2887            keys.add(record.sbn.getKey());
2888            if (record.isIntercepted()) {
2889                interceptedKeys.add(record.sbn.getKey());
2890            }
2891            if (record.getPackageVisibilityOverride()
2892                    != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
2893                visibilityOverrides.putInt(record.sbn.getKey(),
2894                        record.getPackageVisibilityOverride());
2895            }
2896            // Find first min-prio notification for speedbump placement.
2897            if (speedBumpIndex == -1 &&
2898                    // Intrusiveness trumps priority, hence ignore intrusives.
2899                    !record.isRecentlyIntrusive() &&
2900                    // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
2901                    // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
2902                    // (or lower as a safeguard) is sufficient to find the speedbump index.
2903                    // We'll have to revisit this when more package priority buckets are introduced.
2904                    record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
2905                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2906                speedBumpIndex = keys.size() - 1;
2907            }
2908        }
2909        String[] keysAr = keys.toArray(new String[keys.size()]);
2910        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2911        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
2912                speedBumpIndex);
2913    }
2914
2915    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
2916        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
2917            return false;
2918        }
2919        // TODO: remove this for older listeners.
2920        return true;
2921    }
2922
2923    public class NotificationListeners extends ManagedServices {
2924
2925        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
2926        private boolean mNotificationGroupsDesired;
2927
2928        public NotificationListeners() {
2929            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2930        }
2931
2932        @Override
2933        protected Config getConfig() {
2934            Config c = new Config();
2935            c.caption = "notification listener";
2936            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2937            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2938            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2939            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2940            c.clientLabel = R.string.notification_listener_binding_label;
2941            return c;
2942        }
2943
2944        @Override
2945        protected IInterface asInterface(IBinder binder) {
2946            return INotificationListener.Stub.asInterface(binder);
2947        }
2948
2949        @Override
2950        public void onServiceAdded(ManagedServiceInfo info) {
2951            final INotificationListener listener = (INotificationListener) info.service;
2952            final NotificationRankingUpdate update;
2953            synchronized (mNotificationList) {
2954                updateNotificationGroupsDesiredLocked();
2955                update = makeRankingUpdateLocked(info);
2956            }
2957            try {
2958                listener.onListenerConnected(update);
2959            } catch (RemoteException e) {
2960                // we tried
2961            }
2962        }
2963
2964        @Override
2965        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
2966            if (mListenersDisablingEffects.remove(removed)) {
2967                updateListenerHintsLocked();
2968                updateEffectsSuppressorLocked();
2969            }
2970            mLightTrimListeners.remove(removed);
2971            updateNotificationGroupsDesiredLocked();
2972        }
2973
2974        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
2975            if (trim == TRIM_LIGHT) {
2976                mLightTrimListeners.add(info);
2977            } else {
2978                mLightTrimListeners.remove(info);
2979            }
2980        }
2981
2982        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
2983            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
2984
2985        }
2986
2987        /**
2988         * asynchronously notify all listeners about a new notification
2989         *
2990         * <p>
2991         * Also takes care of removing a notification that has been visible to a listener before,
2992         * but isn't anymore.
2993         */
2994        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
2995            // Lazily initialized snapshots of the notification.
2996            StatusBarNotification sbnClone = null;
2997            StatusBarNotification sbnCloneLight = null;
2998
2999            for (final ManagedServiceInfo info : mServices) {
3000                boolean sbnVisible = isVisibleToListener(sbn, info);
3001                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3002                // This notification hasn't been and still isn't visible -> ignore.
3003                if (!oldSbnVisible && !sbnVisible) {
3004                    continue;
3005                }
3006                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3007
3008                // This notification became invisible -> remove the old one.
3009                if (oldSbnVisible && !sbnVisible) {
3010                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3011                    mHandler.post(new Runnable() {
3012                        @Override
3013                        public void run() {
3014                            notifyRemoved(info, oldSbnLightClone, update);
3015                        }
3016                    });
3017                    continue;
3018                }
3019
3020                final int trim = mListeners.getOnNotificationPostedTrim(info);
3021
3022                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3023                    sbnCloneLight = sbn.cloneLight();
3024                } else if (trim == TRIM_FULL && sbnClone == null) {
3025                    sbnClone = sbn.clone();
3026                }
3027                final StatusBarNotification sbnToPost =
3028                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3029
3030                mHandler.post(new Runnable() {
3031                    @Override
3032                    public void run() {
3033                        notifyPosted(info, sbnToPost, update);
3034                    }
3035                });
3036            }
3037        }
3038
3039        /**
3040         * asynchronously notify all listeners about a removed notification
3041         */
3042        public void notifyRemovedLocked(StatusBarNotification sbn) {
3043            // make a copy in case changes are made to the underlying Notification object
3044            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3045            // notification
3046            final StatusBarNotification sbnLight = sbn.cloneLight();
3047            for (final ManagedServiceInfo info : mServices) {
3048                if (!isVisibleToListener(sbn, info)) {
3049                    continue;
3050                }
3051                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3052                mHandler.post(new Runnable() {
3053                    @Override
3054                    public void run() {
3055                        notifyRemoved(info, sbnLight, update);
3056                    }
3057                });
3058            }
3059        }
3060
3061        /**
3062         * asynchronously notify all listeners about a reordering of notifications
3063         */
3064        public void notifyRankingUpdateLocked() {
3065            for (final ManagedServiceInfo serviceInfo : mServices) {
3066                if (!serviceInfo.isEnabledForCurrentProfiles()) {
3067                    continue;
3068                }
3069                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
3070                mHandler.post(new Runnable() {
3071                    @Override
3072                    public void run() {
3073                        notifyRankingUpdate(serviceInfo, update);
3074                    }
3075                });
3076            }
3077        }
3078
3079        public void notifyListenerHintsChangedLocked(final int hints) {
3080            for (final ManagedServiceInfo serviceInfo : mServices) {
3081                if (!serviceInfo.isEnabledForCurrentProfiles()) {
3082                    continue;
3083                }
3084                mHandler.post(new Runnable() {
3085                    @Override
3086                    public void run() {
3087                        notifyListenerHintsChanged(serviceInfo, hints);
3088                    }
3089                });
3090            }
3091        }
3092
3093        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3094            for (final ManagedServiceInfo serviceInfo : mServices) {
3095                if (!serviceInfo.isEnabledForCurrentProfiles()) {
3096                    continue;
3097                }
3098                mHandler.post(new Runnable() {
3099                    @Override
3100                    public void run() {
3101                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3102                    }
3103                });
3104            }
3105        }
3106
3107        private void notifyPosted(final ManagedServiceInfo info,
3108                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
3109            final INotificationListener listener = (INotificationListener)info.service;
3110            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3111            try {
3112                listener.onNotificationPosted(sbnHolder, rankingUpdate);
3113            } catch (RemoteException ex) {
3114                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3115            }
3116        }
3117
3118        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
3119                NotificationRankingUpdate rankingUpdate) {
3120            if (!info.enabledAndUserMatches(sbn.getUserId())) {
3121                return;
3122            }
3123            final INotificationListener listener = (INotificationListener) info.service;
3124            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3125            try {
3126                listener.onNotificationRemoved(sbnHolder, rankingUpdate);
3127            } catch (RemoteException ex) {
3128                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
3129            }
3130        }
3131
3132        private void notifyRankingUpdate(ManagedServiceInfo info,
3133                                         NotificationRankingUpdate rankingUpdate) {
3134            final INotificationListener listener = (INotificationListener) info.service;
3135            try {
3136                listener.onNotificationRankingUpdate(rankingUpdate);
3137            } catch (RemoteException ex) {
3138                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3139            }
3140        }
3141
3142        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
3143            final INotificationListener listener = (INotificationListener) info.service;
3144            try {
3145                listener.onListenerHintsChanged(hints);
3146            } catch (RemoteException ex) {
3147                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
3148            }
3149        }
3150
3151        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3152                int interruptionFilter) {
3153            final INotificationListener listener = (INotificationListener) info.service;
3154            try {
3155                listener.onInterruptionFilterChanged(interruptionFilter);
3156            } catch (RemoteException ex) {
3157                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3158            }
3159        }
3160
3161        private boolean isListenerPackage(String packageName) {
3162            if (packageName == null) {
3163                return false;
3164            }
3165            // TODO: clean up locking object later
3166            synchronized (mNotificationList) {
3167                for (final ManagedServiceInfo serviceInfo : mServices) {
3168                    if (packageName.equals(serviceInfo.component.getPackageName())) {
3169                        return true;
3170                    }
3171                }
3172            }
3173            return false;
3174        }
3175
3176        /**
3177         * Returns whether any of the currently registered listeners wants to receive notification
3178         * groups.
3179         *
3180         * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3181         */
3182        public boolean notificationGroupsDesired() {
3183            return mNotificationGroupsDesired;
3184        }
3185
3186        private void updateNotificationGroupsDesiredLocked() {
3187            mNotificationGroupsDesired = true;
3188            // No listeners, no groups.
3189            if (mServices.isEmpty()) {
3190                mNotificationGroupsDesired = false;
3191                return;
3192            }
3193            // One listener: Check whether it's SysUI.
3194            if (mServices.size() == 1 &&
3195                    mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3196                mNotificationGroupsDesired = false;
3197                return;
3198            }
3199        }
3200    }
3201
3202    public static final class DumpFilter {
3203        public String pkgFilter;
3204        public boolean zen;
3205
3206        public static DumpFilter parseFromArguments(String[] args) {
3207            if (args != null && args.length == 2 && "p".equals(args[0])
3208                    && args[1] != null && !args[1].trim().isEmpty()) {
3209                final DumpFilter filter = new DumpFilter();
3210                filter.pkgFilter = args[1].trim().toLowerCase();
3211                return filter;
3212            }
3213            if (args != null && args.length == 1 && "zen".equals(args[0])) {
3214                final DumpFilter filter = new DumpFilter();
3215                filter.zen = true;
3216                return filter;
3217            }
3218            return null;
3219        }
3220
3221        public boolean matches(StatusBarNotification sbn) {
3222            return zen ? true : sbn != null
3223                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
3224        }
3225
3226        public boolean matches(ComponentName component) {
3227            return zen ? true : component != null && matches(component.getPackageName());
3228        }
3229
3230        public boolean matches(String pkg) {
3231            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3232        }
3233
3234        @Override
3235        public String toString() {
3236            return zen ? "zen" : ('\'' + pkgFilter + '\'');
3237        }
3238    }
3239
3240    /**
3241     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3242     * binder without sending large amounts of data over a oneway transaction.
3243     */
3244    private static final class StatusBarNotificationHolder
3245            extends IStatusBarNotificationHolder.Stub {
3246        private StatusBarNotification mValue;
3247
3248        public StatusBarNotificationHolder(StatusBarNotification value) {
3249            mValue = value;
3250        }
3251
3252        /** Get the held value and clear it. This function should only be called once per holder */
3253        @Override
3254        public StatusBarNotification get() {
3255            StatusBarNotification value = mValue;
3256            mValue = null;
3257            return value;
3258        }
3259    }
3260}
3261