NotificationManagerService.java revision e4ef156b61fcdbeb4eb62a1426e1b56cbd95c1b2
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 org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20import static org.xmlpull.v1.XmlPullParser.END_TAG;
21import static org.xmlpull.v1.XmlPullParser.START_TAG;
22
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.AppGlobals;
26import android.app.AppOpsManager;
27import android.app.IActivityManager;
28import android.app.INotificationManager;
29import android.app.ITransientNotification;
30import android.app.Notification;
31import android.app.PendingIntent;
32import android.app.StatusBarManager;
33import android.content.BroadcastReceiver;
34import android.content.ComponentName;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.pm.ApplicationInfo;
40import android.content.pm.PackageInfo;
41import android.content.pm.PackageManager;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.content.pm.ParceledListSlice;
44import android.content.res.Resources;
45import android.database.ContentObserver;
46import android.media.AudioManager;
47import android.media.IRingtonePlayer;
48import android.net.Uri;
49import android.os.Binder;
50import android.os.Environment;
51import android.os.Handler;
52import android.os.HandlerThread;
53import android.os.IBinder;
54import android.os.IInterface;
55import android.os.Looper;
56import android.os.Message;
57import android.os.Process;
58import android.os.RemoteException;
59import android.os.UserHandle;
60import android.os.Vibrator;
61import android.provider.Settings;
62import android.service.notification.Condition;
63import android.service.notification.IConditionListener;
64import android.service.notification.IConditionProvider;
65import android.service.notification.INotificationListener;
66import android.service.notification.NotificationListenerService;
67import android.service.notification.NotificationRankingUpdate;
68import android.service.notification.StatusBarNotification;
69import android.service.notification.ZenModeConfig;
70import android.telephony.TelephonyManager;
71import android.text.TextUtils;
72import android.util.ArrayMap;
73import android.util.AtomicFile;
74import android.util.Log;
75import android.util.Slog;
76import android.util.Xml;
77import android.view.accessibility.AccessibilityEvent;
78import android.view.accessibility.AccessibilityManager;
79import android.widget.Toast;
80
81import com.android.internal.R;
82import com.android.internal.util.FastXmlSerializer;
83import com.android.server.EventLogTags;
84import com.android.server.SystemService;
85import com.android.server.lights.Light;
86import com.android.server.lights.LightsManager;
87import com.android.server.notification.ManagedServices.ManagedServiceInfo;
88import com.android.server.notification.ManagedServices.UserProfiles;
89import com.android.server.statusbar.StatusBarManagerInternal;
90
91import libcore.io.IoUtils;
92
93import org.xmlpull.v1.XmlPullParser;
94import org.xmlpull.v1.XmlPullParserException;
95import org.xmlpull.v1.XmlSerializer;
96
97import java.io.File;
98import java.io.FileDescriptor;
99import java.io.FileInputStream;
100import java.io.FileNotFoundException;
101import java.io.FileOutputStream;
102import java.io.IOException;
103import java.io.PrintWriter;
104import java.util.ArrayDeque;
105import java.util.ArrayList;
106import java.util.HashSet;
107import java.util.Iterator;
108import java.util.NoSuchElementException;
109
110/** {@hide} */
111public class NotificationManagerService extends SystemService {
112    static final String TAG = "NotificationService";
113    static final boolean DBG = false;
114
115    static final int MAX_PACKAGE_NOTIFICATIONS = 50;
116
117    // message codes
118    static final int MESSAGE_TIMEOUT = 2;
119    static final int MESSAGE_SAVE_POLICY_FILE = 3;
120    static final int MESSAGE_RECONSIDER_RANKING = 4;
121    static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
122    static final int MESSAGE_SEND_RANKING_UPDATE = 6;
123
124    static final int LONG_DELAY = 3500; // 3.5 seconds
125    static final int SHORT_DELAY = 2000; // 2 seconds
126
127    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
128    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
129
130    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
131    static final boolean SCORE_ONGOING_HIGHER = false;
132
133    static final int JUNK_SCORE = -1000;
134    static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
135    static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
136
137    // Notifications with scores below this will not interrupt the user, either via LED or
138    // sound or vibration
139    static final int SCORE_INTERRUPTION_THRESHOLD =
140            Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
141
142    static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
143    static final boolean ENABLE_BLOCKED_TOASTS = true;
144
145    private IActivityManager mAm;
146    AudioManager mAudioManager;
147    StatusBarManagerInternal mStatusBar;
148    Vibrator mVibrator;
149
150    final IBinder mForegroundToken = new Binder();
151    private WorkerHandler mHandler;
152    private final HandlerThread mRankingThread = new HandlerThread("ranker",
153            Process.THREAD_PRIORITY_BACKGROUND);
154
155    private Light mNotificationLight;
156    Light mAttentionLight;
157    private int mDefaultNotificationColor;
158    private int mDefaultNotificationLedOn;
159
160    private int mDefaultNotificationLedOff;
161    private long[] mDefaultVibrationPattern;
162
163    private long[] mFallbackVibrationPattern;
164    private boolean mUseAttentionLight;
165    boolean mSystemReady;
166
167    private boolean mDisableNotificationAlerts;
168    NotificationRecord mSoundNotification;
169    NotificationRecord mVibrateNotification;
170
171    // for enabling and disabling notification pulse behavior
172    private boolean mScreenOn = true;
173    private boolean mInCall = false;
174    private boolean mNotificationPulseEnabled;
175
176    // used as a mutex for access to all active notifications & listeners
177    final ArrayList<NotificationRecord> mNotificationList =
178            new ArrayList<NotificationRecord>();
179    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
180            new ArrayMap<String, NotificationRecord>();
181    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
182
183    ArrayList<String> mLights = new ArrayList<String>();
184    NotificationRecord mLedNotification;
185
186    private AppOpsManager mAppOps;
187
188    private Archive mArchive;
189
190    // Notification control database. For now just contains disabled packages.
191    private AtomicFile mPolicyFile;
192    private HashSet<String> mBlockedPackages = new HashSet<String>();
193
194    private static final int DB_VERSION = 1;
195
196    private static final String TAG_BODY = "notification-policy";
197    private static final String ATTR_VERSION = "version";
198
199    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
200    private static final String TAG_PACKAGE = "package";
201    private static final String ATTR_NAME = "name";
202
203    private RankingHelper mRankingHelper;
204
205    private final UserProfiles mUserProfiles = new UserProfiles();
206    private NotificationListeners mListeners;
207    private ConditionProviders mConditionProviders;
208    private NotificationUsageStats mUsageStats;
209
210    private static final int MY_UID = Process.myUid();
211    private static final int MY_PID = Process.myPid();
212    private static final int REASON_DELEGATE_CLICK = 1;
213    private static final int REASON_DELEGATE_CANCEL = 2;
214    private static final int REASON_DELEGATE_CANCEL_ALL = 3;
215    private static final int REASON_DELEGATE_ERROR = 4;
216    private static final int REASON_PACKAGE_CHANGED = 5;
217    private static final int REASON_USER_STOPPED = 6;
218    private static final int REASON_PACKAGE_BANNED = 7;
219    private static final int REASON_NOMAN_CANCEL = 8;
220    private static final int REASON_NOMAN_CANCEL_ALL = 9;
221    private static final int REASON_LISTENER_CANCEL = 10;
222    private static final int REASON_LISTENER_CANCEL_ALL = 11;
223    private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
224
225    private static class Archive {
226        final int mBufferSize;
227        final ArrayDeque<StatusBarNotification> mBuffer;
228
229        public Archive(int size) {
230            mBufferSize = size;
231            mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
232        }
233
234        public String toString() {
235            final StringBuilder sb = new StringBuilder();
236            final int N = mBuffer.size();
237            sb.append("Archive (");
238            sb.append(N);
239            sb.append(" notification");
240            sb.append((N==1)?")":"s)");
241            return sb.toString();
242        }
243
244        public void record(StatusBarNotification nr) {
245            if (mBuffer.size() == mBufferSize) {
246                mBuffer.removeFirst();
247            }
248
249            // We don't want to store the heavy bits of the notification in the archive,
250            // but other clients in the system process might be using the object, so we
251            // store a (lightened) copy.
252            mBuffer.addLast(nr.cloneLight());
253        }
254
255        public void clear() {
256            mBuffer.clear();
257        }
258
259        public Iterator<StatusBarNotification> descendingIterator() {
260            return mBuffer.descendingIterator();
261        }
262        public Iterator<StatusBarNotification> ascendingIterator() {
263            return mBuffer.iterator();
264        }
265        public Iterator<StatusBarNotification> filter(
266                final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
267            return new Iterator<StatusBarNotification>() {
268                StatusBarNotification mNext = findNext();
269
270                private StatusBarNotification findNext() {
271                    while (iter.hasNext()) {
272                        StatusBarNotification nr = iter.next();
273                        if ((pkg == null || nr.getPackageName() == pkg)
274                                && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
275                            return nr;
276                        }
277                    }
278                    return null;
279                }
280
281                @Override
282                public boolean hasNext() {
283                    return mNext == null;
284                }
285
286                @Override
287                public StatusBarNotification next() {
288                    StatusBarNotification next = mNext;
289                    if (next == null) {
290                        throw new NoSuchElementException();
291                    }
292                    mNext = findNext();
293                    return next;
294                }
295
296                @Override
297                public void remove() {
298                    iter.remove();
299                }
300            };
301        }
302
303        public StatusBarNotification[] getArray(int count) {
304            if (count == 0) count = mBufferSize;
305            final StatusBarNotification[] a
306                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
307            Iterator<StatusBarNotification> iter = descendingIterator();
308            int i=0;
309            while (iter.hasNext() && i < count) {
310                a[i++] = iter.next();
311            }
312            return a;
313        }
314
315        public StatusBarNotification[] getArray(int count, String pkg, int userId) {
316            if (count == 0) count = mBufferSize;
317            final StatusBarNotification[] a
318                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
319            Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
320            int i=0;
321            while (iter.hasNext() && i < count) {
322                a[i++] = iter.next();
323            }
324            return a;
325        }
326
327    }
328
329    private void loadPolicyFile() {
330        synchronized(mPolicyFile) {
331            mBlockedPackages.clear();
332
333            FileInputStream infile = null;
334            try {
335                infile = mPolicyFile.openRead();
336                final XmlPullParser parser = Xml.newPullParser();
337                parser.setInput(infile, null);
338
339                int type;
340                String tag;
341                int version = DB_VERSION;
342                while ((type = parser.next()) != END_DOCUMENT) {
343                    tag = parser.getName();
344                    if (type == START_TAG) {
345                        if (TAG_BODY.equals(tag)) {
346                            version = Integer.parseInt(
347                                    parser.getAttributeValue(null, ATTR_VERSION));
348                        } else if (TAG_BLOCKED_PKGS.equals(tag)) {
349                            while ((type = parser.next()) != END_DOCUMENT) {
350                                tag = parser.getName();
351                                if (TAG_PACKAGE.equals(tag)) {
352                                    mBlockedPackages.add(
353                                            parser.getAttributeValue(null, ATTR_NAME));
354                                } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
355                                    break;
356                                }
357                            }
358                        }
359                    }
360                    mZenModeHelper.readXml(parser);
361                    mRankingHelper.readXml(parser);
362                }
363            } catch (FileNotFoundException e) {
364                // No data yet
365            } catch (IOException e) {
366                Log.wtf(TAG, "Unable to read notification policy", e);
367            } catch (NumberFormatException e) {
368                Log.wtf(TAG, "Unable to parse notification policy", e);
369            } catch (XmlPullParserException e) {
370                Log.wtf(TAG, "Unable to parse notification policy", e);
371            } finally {
372                IoUtils.closeQuietly(infile);
373            }
374        }
375    }
376
377    public void savePolicyFile() {
378        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
379        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
380    }
381
382    private void handleSavePolicyFile() {
383        Slog.d(TAG, "handleSavePolicyFile");
384        synchronized (mPolicyFile) {
385            final FileOutputStream stream;
386            try {
387                stream = mPolicyFile.startWrite();
388            } catch (IOException e) {
389                Slog.w(TAG, "Failed to save policy file", e);
390                return;
391            }
392
393            try {
394                final XmlSerializer out = new FastXmlSerializer();
395                out.setOutput(stream, "utf-8");
396                out.startDocument(null, true);
397                out.startTag(null, TAG_BODY);
398                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
399                mZenModeHelper.writeXml(out);
400                mRankingHelper.writeXml(out);
401                out.endTag(null, TAG_BODY);
402                out.endDocument();
403                mPolicyFile.finishWrite(stream);
404            } catch (IOException e) {
405                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
406                mPolicyFile.failWrite(stream);
407            }
408        }
409    }
410
411    /** Use this when you actually want to post a notification or toast.
412     *
413     * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
414     */
415    private boolean noteNotificationOp(String pkg, int uid) {
416        if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
417                != AppOpsManager.MODE_ALLOWED) {
418            Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
419            return false;
420        }
421        return true;
422    }
423
424    private static final class ToastRecord
425    {
426        final int pid;
427        final String pkg;
428        final ITransientNotification callback;
429        int duration;
430
431        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
432        {
433            this.pid = pid;
434            this.pkg = pkg;
435            this.callback = callback;
436            this.duration = duration;
437        }
438
439        void update(int duration) {
440            this.duration = duration;
441        }
442
443        void dump(PrintWriter pw, String prefix, DumpFilter filter) {
444            if (filter != null && !filter.matches(pkg)) return;
445            pw.println(prefix + this);
446        }
447
448        @Override
449        public final String toString()
450        {
451            return "ToastRecord{"
452                + Integer.toHexString(System.identityHashCode(this))
453                + " pkg=" + pkg
454                + " callback=" + callback
455                + " duration=" + duration;
456        }
457    }
458
459    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
460
461        @Override
462        public void onSetDisabled(int status) {
463            synchronized (mNotificationList) {
464                mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
465                if (mDisableNotificationAlerts) {
466                    // cancel whatever's going on
467                    long identity = Binder.clearCallingIdentity();
468                    try {
469                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
470                        if (player != null) {
471                            player.stopAsync();
472                        }
473                    } catch (RemoteException e) {
474                    } finally {
475                        Binder.restoreCallingIdentity(identity);
476                    }
477
478                    identity = Binder.clearCallingIdentity();
479                    try {
480                        mVibrator.cancel();
481                    } finally {
482                        Binder.restoreCallingIdentity(identity);
483                    }
484                }
485            }
486        }
487
488        @Override
489        public void onClearAll(int callingUid, int callingPid, int userId) {
490            synchronized (mNotificationList) {
491                cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
492                        /*includeCurrentProfiles*/ true);
493            }
494        }
495
496        @Override
497        public void onNotificationClick(int callingUid, int callingPid, String key) {
498            synchronized (mNotificationList) {
499                EventLogTags.writeNotificationClicked(key);
500                NotificationRecord r = mNotificationsByKey.get(key);
501                if (r == null) {
502                    Log.w(TAG, "No notification with key: " + key);
503                    return;
504                }
505                StatusBarNotification sbn = r.sbn;
506                cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
507                        sbn.getId(), Notification.FLAG_AUTO_CANCEL,
508                        Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
509                        REASON_DELEGATE_CLICK, null);
510            }
511        }
512
513        @Override
514        public void onNotificationClear(int callingUid, int callingPid,
515                String pkg, String tag, int id, int userId) {
516            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
517                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
518                    true, userId, REASON_DELEGATE_CANCEL, null);
519        }
520
521        @Override
522        public void onPanelRevealed() {
523            EventLogTags.writeNotificationPanelRevealed();
524            synchronized (mNotificationList) {
525                // sound
526                mSoundNotification = null;
527
528                long identity = Binder.clearCallingIdentity();
529                try {
530                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
531                    if (player != null) {
532                        player.stopAsync();
533                    }
534                } catch (RemoteException e) {
535                } finally {
536                    Binder.restoreCallingIdentity(identity);
537                }
538
539                // vibrate
540                mVibrateNotification = null;
541                identity = Binder.clearCallingIdentity();
542                try {
543                    mVibrator.cancel();
544                } finally {
545                    Binder.restoreCallingIdentity(identity);
546                }
547
548                // light
549                mLights.clear();
550                mLedNotification = null;
551                updateLightsLocked();
552            }
553        }
554
555        @Override
556        public void onPanelHidden() {
557            EventLogTags.writeNotificationPanelHidden();
558        }
559
560        @Override
561        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
562                int uid, int initialPid, String message, int userId) {
563            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
564                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
565            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
566                    REASON_DELEGATE_ERROR, null);
567            long ident = Binder.clearCallingIdentity();
568            try {
569                ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
570                        "Bad notification posted from package " + pkg
571                        + ": " + message);
572            } catch (RemoteException e) {
573            }
574            Binder.restoreCallingIdentity(ident);
575        }
576
577        @Override
578        public boolean allowDisable(int what, IBinder token, String pkg) {
579            return mZenModeHelper.allowDisable(what, token, pkg);
580        }
581
582        @Override
583        public void onNotificationVisibilityChanged(
584                String[] newlyVisibleKeys, String[] noLongerVisibleKeys) {
585            // Using ';' as separator since eventlogs uses ',' to separate
586            // args.
587            EventLogTags.writeNotificationVisibilityChanged(
588                    TextUtils.join(";", newlyVisibleKeys),
589                    TextUtils.join(";", noLongerVisibleKeys));
590            synchronized (mNotificationList) {
591                for (String key : newlyVisibleKeys) {
592                    NotificationRecord r = mNotificationsByKey.get(key);
593                    if (r == null) continue;
594                    r.stats.onVisibilityChanged(true);
595                }
596                // Note that we might receive this event after notifications
597                // have already left the system, e.g. after dismissing from the
598                // shade. Hence not finding notifications in
599                // mNotificationsByKey is not an exceptional condition.
600                for (String key : noLongerVisibleKeys) {
601                    NotificationRecord r = mNotificationsByKey.get(key);
602                    if (r == null) continue;
603                    r.stats.onVisibilityChanged(false);
604                }
605            }
606        }
607    };
608
609    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
610        @Override
611        public void onReceive(Context context, Intent intent) {
612            String action = intent.getAction();
613
614            boolean queryRestart = false;
615            boolean queryRemove = false;
616            boolean packageChanged = false;
617            boolean cancelNotifications = true;
618
619            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
620                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
621                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
622                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
623                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
624                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
625                String pkgList[] = null;
626                boolean queryReplace = queryRemove &&
627                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
628                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
629                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
630                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
631                } else if (queryRestart) {
632                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
633                } else {
634                    Uri uri = intent.getData();
635                    if (uri == null) {
636                        return;
637                    }
638                    String pkgName = uri.getSchemeSpecificPart();
639                    if (pkgName == null) {
640                        return;
641                    }
642                    if (packageChanged) {
643                        // We cancel notifications for packages which have just been disabled
644                        try {
645                            final int enabled = getContext().getPackageManager()
646                                    .getApplicationEnabledSetting(pkgName);
647                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
648                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
649                                cancelNotifications = false;
650                            }
651                        } catch (IllegalArgumentException e) {
652                            // Package doesn't exist; probably racing with uninstall.
653                            // cancelNotifications is already true, so nothing to do here.
654                            if (DBG) {
655                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
656                            }
657                        }
658                    }
659                    pkgList = new String[]{pkgName};
660                }
661
662                if (pkgList != null && (pkgList.length > 0)) {
663                    for (String pkgName : pkgList) {
664                        if (cancelNotifications) {
665                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
666                                    UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null);
667                        }
668                    }
669                }
670                mListeners.onPackagesChanged(queryReplace, pkgList);
671                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
672            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
673                // Keep track of screen on/off state, but do not turn off the notification light
674                // until user passes through the lock screen or views the notification.
675                mScreenOn = true;
676            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
677                mScreenOn = false;
678            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
679                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
680                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
681                updateNotificationPulse();
682            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
683                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
684                if (userHandle >= 0) {
685                    cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
686                            REASON_USER_STOPPED, null);
687                }
688            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
689                // turn off LED when user passes through lock screen
690                mNotificationLight.turnOff();
691            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
692                // reload per-user settings
693                mSettingsObserver.update(null);
694                mUserProfiles.updateCache(context);
695            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
696                mUserProfiles.updateCache(context);
697            }
698        }
699    };
700
701    class SettingsObserver extends ContentObserver {
702        private final Uri NOTIFICATION_LIGHT_PULSE_URI
703                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
704
705        SettingsObserver(Handler handler) {
706            super(handler);
707        }
708
709        void observe() {
710            ContentResolver resolver = getContext().getContentResolver();
711            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
712                    false, this, UserHandle.USER_ALL);
713            update(null);
714        }
715
716        @Override public void onChange(boolean selfChange, Uri uri) {
717            update(uri);
718        }
719
720        public void update(Uri uri) {
721            ContentResolver resolver = getContext().getContentResolver();
722            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
723                boolean pulseEnabled = Settings.System.getInt(resolver,
724                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
725                if (mNotificationPulseEnabled != pulseEnabled) {
726                    mNotificationPulseEnabled = pulseEnabled;
727                    updateNotificationPulse();
728                }
729            }
730        }
731    }
732
733    private SettingsObserver mSettingsObserver;
734    private ZenModeHelper mZenModeHelper;
735
736    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
737        int[] ar = r.getIntArray(resid);
738        if (ar == null) {
739            return def;
740        }
741        final int len = ar.length > maxlen ? maxlen : ar.length;
742        long[] out = new long[len];
743        for (int i=0; i<len; i++) {
744            out[i] = ar[i];
745        }
746        return out;
747    }
748
749    public NotificationManagerService(Context context) {
750        super(context);
751    }
752
753    @Override
754    public void onStart() {
755        Resources resources = getContext().getResources();
756
757        mAm = ActivityManagerNative.getDefault();
758        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
759        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
760
761        mHandler = new WorkerHandler();
762        mRankingThread.start();
763        String[] extractorNames;
764        try {
765            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
766        } catch (Resources.NotFoundException e) {
767            extractorNames = new String[0];
768        }
769        mRankingHelper = new RankingHelper(getContext(),
770                new RankingWorkerHandler(mRankingThread.getLooper()),
771                extractorNames);
772        mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
773        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
774            @Override
775            public void onConfigChanged() {
776                savePolicyFile();
777            }
778        });
779        final File systemDir = new File(Environment.getDataDirectory(), "system");
780        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
781        mUsageStats = new NotificationUsageStats(getContext());
782
783        importOldBlockDb();
784
785        mListeners = new NotificationListeners();
786        mConditionProviders = new ConditionProviders(getContext(),
787                mHandler, mUserProfiles, mZenModeHelper);
788        mStatusBar = getLocalService(StatusBarManagerInternal.class);
789        mStatusBar.setNotificationDelegate(mNotificationDelegate);
790
791        final LightsManager lights = getLocalService(LightsManager.class);
792        mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
793        mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
794
795        mDefaultNotificationColor = resources.getColor(
796                R.color.config_defaultNotificationColor);
797        mDefaultNotificationLedOn = resources.getInteger(
798                R.integer.config_defaultNotificationLedOn);
799        mDefaultNotificationLedOff = resources.getInteger(
800                R.integer.config_defaultNotificationLedOff);
801
802        mDefaultVibrationPattern = getLongArray(resources,
803                R.array.config_defaultNotificationVibePattern,
804                VIBRATE_PATTERN_MAXLEN,
805                DEFAULT_VIBRATE_PATTERN);
806
807        mFallbackVibrationPattern = getLongArray(resources,
808                R.array.config_notificationFallbackVibePattern,
809                VIBRATE_PATTERN_MAXLEN,
810                DEFAULT_VIBRATE_PATTERN);
811
812        mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
813
814        // Don't start allowing notifications until the setup wizard has run once.
815        // After that, including subsequent boots, init with notifications turned on.
816        // This works on the first boot because the setup wizard will toggle this
817        // flag at least once and we'll go back to 0 after that.
818        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
819                    Settings.Global.DEVICE_PROVISIONED, 0)) {
820            mDisableNotificationAlerts = true;
821        }
822        mZenModeHelper.updateZenMode();
823
824        mUserProfiles.updateCache(getContext());
825
826        // register for various Intents
827        IntentFilter filter = new IntentFilter();
828        filter.addAction(Intent.ACTION_SCREEN_ON);
829        filter.addAction(Intent.ACTION_SCREEN_OFF);
830        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
831        filter.addAction(Intent.ACTION_USER_PRESENT);
832        filter.addAction(Intent.ACTION_USER_STOPPED);
833        filter.addAction(Intent.ACTION_USER_SWITCHED);
834        filter.addAction(Intent.ACTION_USER_ADDED);
835        getContext().registerReceiver(mIntentReceiver, filter);
836        IntentFilter pkgFilter = new IntentFilter();
837        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
838        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
839        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
840        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
841        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
842        pkgFilter.addDataScheme("package");
843        getContext().registerReceiver(mIntentReceiver, pkgFilter);
844        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
845        getContext().registerReceiver(mIntentReceiver, sdFilter);
846
847        mSettingsObserver = new SettingsObserver(mHandler);
848
849        mArchive = new Archive(resources.getInteger(
850                R.integer.config_notificationServiceArchiveSize));
851
852        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
853        publishLocalService(NotificationManagerInternal.class, mInternalService);
854    }
855
856    /**
857     * Read the old XML-based app block database and import those blockages into the AppOps system.
858     */
859    private void importOldBlockDb() {
860        loadPolicyFile();
861
862        PackageManager pm = getContext().getPackageManager();
863        for (String pkg : mBlockedPackages) {
864            PackageInfo info = null;
865            try {
866                info = pm.getPackageInfo(pkg, 0);
867                setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
868            } catch (NameNotFoundException e) {
869                // forget you
870            }
871        }
872        mBlockedPackages.clear();
873    }
874
875    @Override
876    public void onBootPhase(int phase) {
877        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
878            // no beeping until we're basically done booting
879            mSystemReady = true;
880
881            // Grab our optional AudioService
882            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
883            mZenModeHelper.setAudioManager(mAudioManager);
884        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
885            // This observer will force an update when observe is called, causing us to
886            // bind to listener services.
887            mSettingsObserver.observe();
888            mListeners.onBootPhaseAppsCanStart();
889            mConditionProviders.onBootPhaseAppsCanStart();
890        }
891    }
892
893    void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
894        Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
895
896        mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
897                enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
898
899        // Now, cancel any outstanding notifications that are part of a just-disabled app
900        if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
901            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
902                    REASON_PACKAGE_BANNED, null);
903        }
904    }
905
906    private final IBinder mService = new INotificationManager.Stub() {
907        // Toasts
908        // ============================================================================
909
910        @Override
911        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
912        {
913            if (DBG) {
914                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
915                        + " duration=" + duration);
916            }
917
918            if (pkg == null || callback == null) {
919                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
920                return ;
921            }
922
923            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
924
925            if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
926                if (!isSystemToast) {
927                    Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
928                    return;
929                }
930            }
931
932            synchronized (mToastQueue) {
933                int callingPid = Binder.getCallingPid();
934                long callingId = Binder.clearCallingIdentity();
935                try {
936                    ToastRecord record;
937                    int index = indexOfToastLocked(pkg, callback);
938                    // If it's already in the queue, we update it in place, we don't
939                    // move it to the end of the queue.
940                    if (index >= 0) {
941                        record = mToastQueue.get(index);
942                        record.update(duration);
943                    } else {
944                        // Limit the number of toasts that any given package except the android
945                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
946                        if (!isSystemToast) {
947                            int count = 0;
948                            final int N = mToastQueue.size();
949                            for (int i=0; i<N; i++) {
950                                 final ToastRecord r = mToastQueue.get(i);
951                                 if (r.pkg.equals(pkg)) {
952                                     count++;
953                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
954                                         Slog.e(TAG, "Package has already posted " + count
955                                                + " toasts. Not showing more. Package=" + pkg);
956                                         return;
957                                     }
958                                 }
959                            }
960                        }
961
962                        record = new ToastRecord(callingPid, pkg, callback, duration);
963                        mToastQueue.add(record);
964                        index = mToastQueue.size() - 1;
965                        keepProcessAliveLocked(callingPid);
966                    }
967                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
968                    // new or just been updated.  Call back and tell it to show itself.
969                    // If the callback fails, this will remove it from the list, so don't
970                    // assume that it's valid after this.
971                    if (index == 0) {
972                        showNextToastLocked();
973                    }
974                } finally {
975                    Binder.restoreCallingIdentity(callingId);
976                }
977            }
978        }
979
980        @Override
981        public void cancelToast(String pkg, ITransientNotification callback) {
982            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
983
984            if (pkg == null || callback == null) {
985                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
986                return ;
987            }
988
989            synchronized (mToastQueue) {
990                long callingId = Binder.clearCallingIdentity();
991                try {
992                    int index = indexOfToastLocked(pkg, callback);
993                    if (index >= 0) {
994                        cancelToastLocked(index);
995                    } else {
996                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
997                                + " callback=" + callback);
998                    }
999                } finally {
1000                    Binder.restoreCallingIdentity(callingId);
1001                }
1002            }
1003        }
1004
1005        @Override
1006        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1007                Notification notification, int[] idOut, int userId) throws RemoteException {
1008            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1009                    Binder.getCallingPid(), tag, id, notification, idOut, userId);
1010        }
1011
1012        @Override
1013        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1014            checkCallerIsSystemOrSameApp(pkg);
1015            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1016                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1017            // Don't allow client applications to cancel foreground service notis.
1018            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1019                    Binder.getCallingUid() == Process.SYSTEM_UID
1020                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1021                    null);
1022        }
1023
1024        @Override
1025        public void cancelAllNotifications(String pkg, int userId) {
1026            checkCallerIsSystemOrSameApp(pkg);
1027
1028            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1029                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1030
1031            // Calling from user space, don't allow the canceling of actively
1032            // running foreground services.
1033            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1034                    pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1035                    REASON_NOMAN_CANCEL_ALL, null);
1036        }
1037
1038        @Override
1039        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1040            checkCallerIsSystem();
1041
1042            setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1043        }
1044
1045        /**
1046         * Use this when you just want to know if notifications are OK for this package.
1047         */
1048        @Override
1049        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1050            checkCallerIsSystem();
1051            return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1052                    == AppOpsManager.MODE_ALLOWED);
1053        }
1054
1055        @Override
1056        public void setPackagePriority(String pkg, int uid, int priority) {
1057            checkCallerIsSystem();
1058            mRankingHelper.setPackagePriority(pkg, uid, priority);
1059            savePolicyFile();
1060        }
1061
1062        @Override
1063        public int getPackagePriority(String pkg, int uid) {
1064            checkCallerIsSystem();
1065            return mRankingHelper.getPackagePriority(pkg, uid);
1066        }
1067
1068        /**
1069         * System-only API for getting a list of current (i.e. not cleared) notifications.
1070         *
1071         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1072         * @returns A list of all the notifications, in natural order.
1073         */
1074        @Override
1075        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1076            // enforce() will ensure the calling uid has the correct permission
1077            getContext().enforceCallingOrSelfPermission(
1078                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1079                    "NotificationManagerService.getActiveNotifications");
1080
1081            StatusBarNotification[] tmp = null;
1082            int uid = Binder.getCallingUid();
1083
1084            // noteOp will check to make sure the callingPkg matches the uid
1085            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1086                    == AppOpsManager.MODE_ALLOWED) {
1087                synchronized (mNotificationList) {
1088                    tmp = new StatusBarNotification[mNotificationList.size()];
1089                    final int N = mNotificationList.size();
1090                    for (int i=0; i<N; i++) {
1091                        tmp[i] = mNotificationList.get(i).sbn;
1092                    }
1093                }
1094            }
1095            return tmp;
1096        }
1097
1098        /**
1099         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1100         *
1101         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1102         */
1103        @Override
1104        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1105            // enforce() will ensure the calling uid has the correct permission
1106            getContext().enforceCallingOrSelfPermission(
1107                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1108                    "NotificationManagerService.getHistoricalNotifications");
1109
1110            StatusBarNotification[] tmp = null;
1111            int uid = Binder.getCallingUid();
1112
1113            // noteOp will check to make sure the callingPkg matches the uid
1114            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1115                    == AppOpsManager.MODE_ALLOWED) {
1116                synchronized (mArchive) {
1117                    tmp = mArchive.getArray(count);
1118                }
1119            }
1120            return tmp;
1121        }
1122
1123        /**
1124         * Register a listener binder directly with the notification manager.
1125         *
1126         * Only works with system callers. Apps should extend
1127         * {@link android.service.notification.NotificationListenerService}.
1128         */
1129        @Override
1130        public void registerListener(final INotificationListener listener,
1131                final ComponentName component, final int userid) {
1132            enforceSystemOrSystemUI("INotificationManager.registerListener");
1133            mListeners.registerService(listener, component, userid);
1134        }
1135
1136        /**
1137         * Remove a listener binder directly
1138         */
1139        @Override
1140        public void unregisterListener(INotificationListener listener, int userid) {
1141            mListeners.unregisterService(listener, userid);
1142        }
1143
1144        /**
1145         * Allow an INotificationListener to simulate a "clear all" operation.
1146         *
1147         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1148         *
1149         * @param token The binder for the listener, to check that the caller is allowed
1150         */
1151        @Override
1152        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1153            final int callingUid = Binder.getCallingUid();
1154            final int callingPid = Binder.getCallingPid();
1155            long identity = Binder.clearCallingIdentity();
1156            try {
1157                synchronized (mNotificationList) {
1158                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1159                    if (keys != null) {
1160                        final int N = keys.length;
1161                        for (int i = 0; i < N; i++) {
1162                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1163                            final int userId = r.sbn.getUserId();
1164                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1165                                    !mUserProfiles.isCurrentProfile(userId)) {
1166                                throw new SecurityException("Disallowed call from listener: "
1167                                        + info.service);
1168                            }
1169                            if (r != null) {
1170                                cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1171                                        r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1172                                        userId);
1173                            }
1174                        }
1175                    } else {
1176                        cancelAllLocked(callingUid, callingPid, info.userid,
1177                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1178                    }
1179                }
1180            } finally {
1181                Binder.restoreCallingIdentity(identity);
1182            }
1183        }
1184
1185        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1186                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1187            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1188                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1189                    true,
1190                    userId, REASON_LISTENER_CANCEL, info);
1191        }
1192
1193        /**
1194         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1195         *
1196         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1197         *
1198         * @param token The binder for the listener, to check that the caller is allowed
1199         */
1200        @Override
1201        public void cancelNotificationFromListener(INotificationListener token, String pkg,
1202                String tag, int id) {
1203            final int callingUid = Binder.getCallingUid();
1204            final int callingPid = Binder.getCallingPid();
1205            long identity = Binder.clearCallingIdentity();
1206            try {
1207                synchronized (mNotificationList) {
1208                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1209                    if (info.supportsProfiles()) {
1210                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1211                                + "from " + info.component
1212                                + " use cancelNotification(key) instead.");
1213                    } else {
1214                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1215                                pkg, tag, id, info.userid);
1216                    }
1217                }
1218            } finally {
1219                Binder.restoreCallingIdentity(identity);
1220            }
1221        }
1222
1223        /**
1224         * Allow an INotificationListener to request the list of outstanding notifications seen by
1225         * the current user. Useful when starting up, after which point the listener callbacks
1226         * should be used.
1227         *
1228         * @param token The binder for the listener, to check that the caller is allowed
1229         * @returns The return value will contain the notifications specified in keys, in that
1230         *      order, or if keys is null, all the notifications, in natural order.
1231         */
1232        @Override
1233        public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1234                INotificationListener token) {
1235            synchronized (mNotificationList) {
1236                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1237                final ArrayList<StatusBarNotification> list
1238                        = new ArrayList<StatusBarNotification>();
1239                final int N = mNotificationList.size();
1240                for (int i=0; i<N; i++) {
1241                    StatusBarNotification sbn = mNotificationList.get(i).sbn;
1242                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1243                        list.add(sbn);
1244                    }
1245                }
1246                return new ParceledListSlice<StatusBarNotification>(list);
1247            }
1248        }
1249
1250        @Override
1251        public ZenModeConfig getZenModeConfig() {
1252            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
1253            return mZenModeHelper.getConfig();
1254        }
1255
1256        @Override
1257        public boolean setZenModeConfig(ZenModeConfig config) {
1258            checkCallerIsSystem();
1259            return mZenModeHelper.setConfig(config);
1260        }
1261
1262        @Override
1263        public void notifyConditions(String pkg, IConditionProvider provider,
1264                Condition[] conditions) {
1265            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1266            checkCallerIsSystemOrSameApp(pkg);
1267            final long identity = Binder.clearCallingIdentity();
1268            try {
1269                mConditionProviders.notifyConditions(pkg, info, conditions);
1270            } finally {
1271                Binder.restoreCallingIdentity(identity);
1272            }
1273        }
1274
1275        @Override
1276        public void requestZenModeConditions(IConditionListener callback, int relevance) {
1277            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
1278            mConditionProviders.requestZenModeConditions(callback, relevance);
1279        }
1280
1281        @Override
1282        public void setZenModeCondition(Uri conditionId) {
1283            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
1284            final long identity = Binder.clearCallingIdentity();
1285            try {
1286                mConditionProviders.setZenModeCondition(conditionId);
1287            } finally {
1288                Binder.restoreCallingIdentity(identity);
1289            }
1290        }
1291
1292        @Override
1293        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1294            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1295            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1296        }
1297
1298        @Override
1299        public Condition[] getAutomaticZenModeConditions() {
1300            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1301            return mConditionProviders.getAutomaticZenModeConditions();
1302        }
1303
1304        private void enforceSystemOrSystemUI(String message) {
1305            if (isCallerSystem()) return;
1306            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1307                    message);
1308        }
1309
1310        @Override
1311        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1312            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1313                    != PackageManager.PERMISSION_GRANTED) {
1314                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1315                        + Binder.getCallingPid()
1316                        + ", uid=" + Binder.getCallingUid());
1317                return;
1318            }
1319
1320            dumpImpl(pw, DumpFilter.parseFromArguments(args));
1321        }
1322    };
1323
1324    private String[] getActiveNotificationKeys(INotificationListener token) {
1325        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1326        final ArrayList<String> keys = new ArrayList<String>();
1327        if (info.isEnabledForCurrentProfiles()) {
1328            synchronized (mNotificationList) {
1329                final int N = mNotificationList.size();
1330                for (int i = 0; i < N; i++) {
1331                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1332                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1333                        keys.add(sbn.getKey());
1334                    }
1335                }
1336            }
1337        }
1338        return keys.toArray(new String[keys.size()]);
1339    }
1340
1341    void dumpImpl(PrintWriter pw, DumpFilter filter) {
1342        pw.print("Current Notification Manager state");
1343        if (filter != null) {
1344            pw.print(" (filtered to '"); pw.print(filter.pkgFilter); pw.print("')");
1345        }
1346        pw.println(':');
1347        int N;
1348
1349        synchronized (mToastQueue) {
1350            N = mToastQueue.size();
1351            if (N > 0) {
1352                pw.println("  Toast Queue:");
1353                for (int i=0; i<N; i++) {
1354                    mToastQueue.get(i).dump(pw, "    ", filter);
1355                }
1356                pw.println("  ");
1357            }
1358        }
1359
1360        synchronized (mNotificationList) {
1361            N = mNotificationList.size();
1362            if (N > 0) {
1363                pw.println("  Notification List:");
1364                for (int i=0; i<N; i++) {
1365                    final NotificationRecord nr = mNotificationList.get(i);
1366                    if (filter != null && !filter.matches(nr.sbn)) continue;
1367                    nr.dump(pw, "    ", getContext());
1368                }
1369                pw.println("  ");
1370            }
1371
1372            if (filter == null) {
1373                N = mLights.size();
1374                if (N > 0) {
1375                    pw.println("  Lights List:");
1376                    for (int i=0; i<N; i++) {
1377                        pw.println("    " + mLights.get(i));
1378                    }
1379                    pw.println("  ");
1380                }
1381
1382                pw.println("  mSoundNotification=" + mSoundNotification);
1383                pw.println("  mVibrateNotification=" + mVibrateNotification);
1384                pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1385                pw.println("  mSystemReady=" + mSystemReady);
1386            }
1387            pw.println("  mArchive=" + mArchive.toString());
1388            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1389            int i=0;
1390            while (iter.hasNext()) {
1391                final StatusBarNotification sbn = iter.next();
1392                if (filter != null && !filter.matches(sbn)) continue;
1393                pw.println("    " + sbn);
1394                if (++i >= 5) {
1395                    if (iter.hasNext()) pw.println("    ...");
1396                    break;
1397                }
1398            }
1399
1400            pw.println("\n  Usage Stats:");
1401            mUsageStats.dump(pw, "    ", filter);
1402
1403            if (filter == null) {
1404                pw.println("\n  Zen Mode:");
1405                mZenModeHelper.dump(pw, "    ");
1406            }
1407
1408            pw.println("\n  Ranking Config:");
1409            mRankingHelper.dump(pw, "    ", filter);
1410
1411            pw.println("\n  Notification listeners:");
1412            mListeners.dump(pw, filter);
1413
1414            pw.println("\n  Condition providers:");
1415            mConditionProviders.dump(pw, filter);
1416        }
1417    }
1418
1419    /**
1420     * The private API only accessible to the system process.
1421     */
1422    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1423        @Override
1424        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1425                String tag, int id, Notification notification, int[] idReceived, int userId) {
1426            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1427                    idReceived, userId);
1428        }
1429    };
1430
1431    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1432            final int callingPid, final String tag, final int id, final Notification notification,
1433            int[] idOut, int incomingUserId) {
1434        if (DBG) {
1435            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1436                    + " notification=" + notification);
1437        }
1438        checkCallerIsSystemOrSameApp(pkg);
1439        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1440
1441        final int userId = ActivityManager.handleIncomingUser(callingPid,
1442                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1443        final UserHandle user = new UserHandle(userId);
1444
1445        // Limit the number of notifications that any given package except the android
1446        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1447        if (!isSystemNotification) {
1448            synchronized (mNotificationList) {
1449                int count = 0;
1450                final int N = mNotificationList.size();
1451                for (int i=0; i<N; i++) {
1452                    final NotificationRecord r = mNotificationList.get(i);
1453                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1454                        count++;
1455                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1456                            Slog.e(TAG, "Package has already posted " + count
1457                                    + " notifications.  Not showing more.  package=" + pkg);
1458                            return;
1459                        }
1460                    }
1461                }
1462            }
1463        }
1464
1465        // This conditional is a dirty hack to limit the logging done on
1466        //     behalf of the download manager without affecting other apps.
1467        if (!pkg.equals("com.android.providers.downloads")
1468                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1469            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1470                    pkg, id, tag, userId, notification.toString());
1471        }
1472
1473        if (pkg == null || notification == null) {
1474            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1475                    + " id=" + id + " notification=" + notification);
1476        }
1477        if (notification.icon != 0) {
1478            if (notification.contentView == null) {
1479                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1480                        + " id=" + id + " notification=" + notification);
1481            }
1482        }
1483
1484        mHandler.post(new Runnable() {
1485            @Override
1486            public void run() {
1487
1488                // === Scoring ===
1489
1490                // 0. Sanitize inputs
1491                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1492                        Notification.PRIORITY_MAX);
1493                // Migrate notification flags to scores
1494                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1495                    if (notification.priority < Notification.PRIORITY_MAX) {
1496                        notification.priority = Notification.PRIORITY_MAX;
1497                    }
1498                } else if (SCORE_ONGOING_HIGHER &&
1499                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1500                    if (notification.priority < Notification.PRIORITY_HIGH) {
1501                        notification.priority = Notification.PRIORITY_HIGH;
1502                    }
1503                }
1504
1505                // 1. initial score: buckets of 10, around the app [-20..20]
1506                final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
1507
1508                // 2. extract ranking signals from the notification data
1509                final StatusBarNotification n = new StatusBarNotification(
1510                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1511                        user);
1512                NotificationRecord r = new NotificationRecord(n, score);
1513                NotificationRecord old = mNotificationsByKey.get(n.getKey());
1514                if (old != null) {
1515                    // Retain ranking information from previous record
1516                    r.copyRankingInformation(old);
1517                }
1518                mRankingHelper.extractSignals(r);
1519
1520                // 3. Apply local rules
1521
1522                // blocked apps
1523                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1524                    if (!isSystemNotification) {
1525                        r.score = JUNK_SCORE;
1526                        Slog.e(TAG, "Suppressing notification from package " + pkg
1527                                + " by user request.");
1528                    }
1529                }
1530
1531                if (r.score < SCORE_DISPLAY_THRESHOLD) {
1532                    // Notification will be blocked because the score is too low.
1533                    return;
1534                }
1535
1536                synchronized (mNotificationList) {
1537                    int index = indexOfNotificationLocked(n.getKey());
1538                    if (index < 0) {
1539                        mNotificationList.add(r);
1540                        mUsageStats.registerPostedByApp(r);
1541                    } else {
1542                        old = mNotificationList.get(index);
1543                        mNotificationList.set(index, r);
1544                        mUsageStats.registerUpdatedByApp(r, old);
1545                        // Make sure we don't lose the foreground service state.
1546                        notification.flags |=
1547                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1548                        mNotificationsByKey.remove(old.sbn.getKey());
1549                        r.isUpdate = true;
1550                    }
1551
1552                    mNotificationsByKey.put(n.getKey(), r);
1553
1554                    // Ensure if this is a foreground service that the proper additional
1555                    // flags are set.
1556                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1557                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1558                                | Notification.FLAG_NO_CLEAR;
1559                    }
1560
1561                    applyZenModeLocked(r);
1562
1563                    mRankingHelper.sort(mNotificationList);
1564
1565                    if (notification.icon != 0) {
1566                        mListeners.notifyPostedLocked(n);
1567                    } else {
1568                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1569                        if (old != null && !old.isCanceled) {
1570                            mListeners.notifyRemovedLocked(n);
1571                        }
1572                        // ATTENTION: in a future release we will bail out here
1573                        // so that we do not play sounds, show lights, etc. for invalid
1574                        // notifications
1575                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1576                                + n.getPackageName());
1577                    }
1578
1579                    buzzBeepBlinkLocked(r);
1580                }
1581            }
1582        });
1583
1584        idOut[0] = id;
1585    }
1586
1587    private void buzzBeepBlinkLocked(NotificationRecord record) {
1588        final Notification notification = record.sbn.getNotification();
1589
1590        // Should this notification make noise, vibe, or use the LED?
1591        final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
1592                !record.isIntercepted();
1593        if (DBG || record.isIntercepted())
1594            Slog.v(TAG,
1595                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
1596                            " intercept=" + record.isIntercepted()
1597            );
1598
1599        final int currentUser;
1600        final long token = Binder.clearCallingIdentity();
1601        try {
1602            currentUser = ActivityManager.getCurrentUser();
1603        } finally {
1604            Binder.restoreCallingIdentity(token);
1605        }
1606
1607        // If we're not supposed to beep, vibrate, etc. then don't.
1608        if (!mDisableNotificationAlerts
1609                && (!(record.isUpdate
1610                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1611                && (record.getUserId() == UserHandle.USER_ALL ||
1612                    record.getUserId() == currentUser ||
1613                    mUserProfiles.isCurrentProfile(record.getUserId()))
1614                && canInterrupt
1615                && mSystemReady
1616                && mAudioManager != null) {
1617            if (DBG) Slog.v(TAG, "Interrupting!");
1618
1619            sendAccessibilityEvent(notification, record.sbn.getPackageName());
1620
1621            // sound
1622
1623            // should we use the default notification sound? (indicated either by
1624            // DEFAULT_SOUND or because notification.sound is pointing at
1625            // Settings.System.NOTIFICATION_SOUND)
1626            final boolean useDefaultSound =
1627                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1628                           Settings.System.DEFAULT_NOTIFICATION_URI
1629                                   .equals(notification.sound);
1630
1631            Uri soundUri = null;
1632            boolean hasValidSound = false;
1633
1634            if (useDefaultSound) {
1635                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1636
1637                // check to see if the default notification sound is silent
1638                ContentResolver resolver = getContext().getContentResolver();
1639                hasValidSound = Settings.System.getString(resolver,
1640                       Settings.System.NOTIFICATION_SOUND) != null;
1641            } else if (notification.sound != null) {
1642                soundUri = notification.sound;
1643                hasValidSound = (soundUri != null);
1644            }
1645
1646            if (hasValidSound) {
1647                boolean looping =
1648                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
1649                int audioStreamType;
1650                if (notification.audioStreamType >= 0) {
1651                    audioStreamType = notification.audioStreamType;
1652                } else {
1653                    audioStreamType = DEFAULT_STREAM_TYPE;
1654                }
1655                mSoundNotification = record;
1656                // do not play notifications if stream volume is 0 (typically because
1657                // ringer mode is silent) or if there is a user of exclusive audio focus
1658                if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1659                        && !mAudioManager.isAudioFocusExclusive()) {
1660                    final long identity = Binder.clearCallingIdentity();
1661                    try {
1662                        final IRingtonePlayer player =
1663                                mAudioManager.getRingtonePlayer();
1664                        if (player != null) {
1665                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1666                                    + " on stream " + audioStreamType);
1667                            player.playAsync(soundUri, record.sbn.getUser(), looping,
1668                                    audioStreamType);
1669                        }
1670                    } catch (RemoteException e) {
1671                    } finally {
1672                        Binder.restoreCallingIdentity(identity);
1673                    }
1674                }
1675            }
1676
1677            // vibrate
1678            // Does the notification want to specify its own vibration?
1679            final boolean hasCustomVibrate = notification.vibrate != null;
1680
1681            // new in 4.2: if there was supposed to be a sound and we're in vibrate
1682            // mode, and no other vibration is specified, we fall back to vibration
1683            final boolean convertSoundToVibration =
1684                       !hasCustomVibrate
1685                    && hasValidSound
1686                    && (mAudioManager.getRingerMode()
1687                               == AudioManager.RINGER_MODE_VIBRATE);
1688
1689            // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1690            final boolean useDefaultVibrate =
1691                    (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1692
1693            if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1694                    && !(mAudioManager.getRingerMode()
1695                            == AudioManager.RINGER_MODE_SILENT)) {
1696                mVibrateNotification = record;
1697
1698                if (useDefaultVibrate || convertSoundToVibration) {
1699                    // Escalate privileges so we can use the vibrator even if the
1700                    // notifying app does not have the VIBRATE permission.
1701                    long identity = Binder.clearCallingIdentity();
1702                    try {
1703                        mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1704                            useDefaultVibrate ? mDefaultVibrationPattern
1705                                : mFallbackVibrationPattern,
1706                            ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1707                                    ? 0: -1, notification.audioStreamType);
1708                    } finally {
1709                        Binder.restoreCallingIdentity(identity);
1710                    }
1711                } else if (notification.vibrate.length > 1) {
1712                    // If you want your own vibration pattern, you need the VIBRATE
1713                    // permission
1714                    mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
1715                            notification.vibrate,
1716                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1717                                ? 0: -1, notification.audioStreamType);
1718                }
1719            }
1720        }
1721
1722        // light
1723        // release the light
1724        boolean wasShowLights = mLights.remove(record.getKey());
1725        if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
1726            mLedNotification = null;
1727        }
1728        if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
1729            mLights.add(record.getKey());
1730            updateLightsLocked();
1731            if (mUseAttentionLight) {
1732                mAttentionLight.pulse();
1733            }
1734        } else if (wasShowLights) {
1735            updateLightsLocked();
1736        }
1737    }
1738
1739    void showNextToastLocked() {
1740        ToastRecord record = mToastQueue.get(0);
1741        while (record != null) {
1742            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1743            try {
1744                record.callback.show();
1745                scheduleTimeoutLocked(record);
1746                return;
1747            } catch (RemoteException e) {
1748                Slog.w(TAG, "Object died trying to show notification " + record.callback
1749                        + " in package " + record.pkg);
1750                // remove it from the list and let the process die
1751                int index = mToastQueue.indexOf(record);
1752                if (index >= 0) {
1753                    mToastQueue.remove(index);
1754                }
1755                keepProcessAliveLocked(record.pid);
1756                if (mToastQueue.size() > 0) {
1757                    record = mToastQueue.get(0);
1758                } else {
1759                    record = null;
1760                }
1761            }
1762        }
1763    }
1764
1765    void cancelToastLocked(int index) {
1766        ToastRecord record = mToastQueue.get(index);
1767        try {
1768            record.callback.hide();
1769        } catch (RemoteException e) {
1770            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1771                    + " in package " + record.pkg);
1772            // don't worry about this, we're about to remove it from
1773            // the list anyway
1774        }
1775        mToastQueue.remove(index);
1776        keepProcessAliveLocked(record.pid);
1777        if (mToastQueue.size() > 0) {
1778            // Show the next one. If the callback fails, this will remove
1779            // it from the list, so don't assume that the list hasn't changed
1780            // after this point.
1781            showNextToastLocked();
1782        }
1783    }
1784
1785    private void scheduleTimeoutLocked(ToastRecord r)
1786    {
1787        mHandler.removeCallbacksAndMessages(r);
1788        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1789        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1790        mHandler.sendMessageDelayed(m, delay);
1791    }
1792
1793    private void handleTimeout(ToastRecord record)
1794    {
1795        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1796        synchronized (mToastQueue) {
1797            int index = indexOfToastLocked(record.pkg, record.callback);
1798            if (index >= 0) {
1799                cancelToastLocked(index);
1800            }
1801        }
1802    }
1803
1804    // lock on mToastQueue
1805    int indexOfToastLocked(String pkg, ITransientNotification callback)
1806    {
1807        IBinder cbak = callback.asBinder();
1808        ArrayList<ToastRecord> list = mToastQueue;
1809        int len = list.size();
1810        for (int i=0; i<len; i++) {
1811            ToastRecord r = list.get(i);
1812            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1813                return i;
1814            }
1815        }
1816        return -1;
1817    }
1818
1819    // lock on mToastQueue
1820    void keepProcessAliveLocked(int pid)
1821    {
1822        int toastCount = 0; // toasts from this pid
1823        ArrayList<ToastRecord> list = mToastQueue;
1824        int N = list.size();
1825        for (int i=0; i<N; i++) {
1826            ToastRecord r = list.get(i);
1827            if (r.pid == pid) {
1828                toastCount++;
1829            }
1830        }
1831        try {
1832            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1833        } catch (RemoteException e) {
1834            // Shouldn't happen.
1835        }
1836    }
1837
1838    private void handleRankingReconsideration(Message message) {
1839        if (!(message.obj instanceof RankingReconsideration)) return;
1840        RankingReconsideration recon = (RankingReconsideration) message.obj;
1841        recon.run();
1842        boolean changed;
1843        synchronized (mNotificationList) {
1844            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
1845            if (record == null) {
1846                return;
1847            }
1848            int indexBefore = findNotificationRecordIndexLocked(record);
1849            boolean interceptBefore = record.isIntercepted();
1850            recon.applyChangesLocked(record);
1851            applyZenModeLocked(record);
1852            mRankingHelper.sort(mNotificationList);
1853            int indexAfter = findNotificationRecordIndexLocked(record);
1854            boolean interceptAfter = record.isIntercepted();
1855            changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
1856            if (interceptBefore && !interceptAfter) {
1857                buzzBeepBlinkLocked(record);
1858            }
1859        }
1860        if (changed) {
1861            scheduleSendRankingUpdate();
1862        }
1863    }
1864
1865    private void handleRankingConfigChange() {
1866        synchronized (mNotificationList) {
1867            final int N = mNotificationList.size();
1868            ArrayList<String> orderBefore = new ArrayList<String>(N);
1869            for (int i = 0; i < N; i++) {
1870                final NotificationRecord r = mNotificationList.get(i);
1871                orderBefore.add(r.getKey());
1872                mRankingHelper.extractSignals(r);
1873            }
1874            mRankingHelper.sort(mNotificationList);
1875            for (int i = 0; i < N; i++) {
1876                if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
1877                    scheduleSendRankingUpdate();
1878                    return;
1879                }
1880            }
1881        }
1882    }
1883
1884    // let zen mode evaluate this record
1885    private void applyZenModeLocked(NotificationRecord record) {
1886        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
1887    }
1888
1889    // lock on mNotificationList
1890    private int findNotificationRecordIndexLocked(NotificationRecord target) {
1891        return mRankingHelper.indexOf(mNotificationList, target);
1892    }
1893
1894    private void scheduleSendRankingUpdate() {
1895        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
1896        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
1897        mHandler.sendMessage(m);
1898    }
1899
1900    private void handleSendRankingUpdate() {
1901        synchronized (mNotificationList) {
1902            mListeners.notifyRankingUpdateLocked();
1903        }
1904    }
1905
1906    private final class WorkerHandler extends Handler
1907    {
1908        @Override
1909        public void handleMessage(Message msg)
1910        {
1911            switch (msg.what)
1912            {
1913                case MESSAGE_TIMEOUT:
1914                    handleTimeout((ToastRecord)msg.obj);
1915                    break;
1916                case MESSAGE_SAVE_POLICY_FILE:
1917                    handleSavePolicyFile();
1918                    break;
1919                case MESSAGE_SEND_RANKING_UPDATE:
1920                    handleSendRankingUpdate();
1921                    break;
1922            }
1923        }
1924
1925    }
1926
1927    private final class RankingWorkerHandler extends Handler
1928    {
1929        public RankingWorkerHandler(Looper looper) {
1930            super(looper);
1931        }
1932
1933        @Override
1934        public void handleMessage(Message msg) {
1935            switch (msg.what) {
1936                case MESSAGE_RECONSIDER_RANKING:
1937                    handleRankingReconsideration(msg);
1938                    break;
1939                case MESSAGE_RANKING_CONFIG_CHANGE:
1940                    handleRankingConfigChange();
1941                    break;
1942            }
1943        }
1944    }
1945
1946    // Notifications
1947    // ============================================================================
1948    static int clamp(int x, int low, int high) {
1949        return (x < low) ? low : ((x > high) ? high : x);
1950    }
1951
1952    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
1953        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
1954        if (!manager.isEnabled()) {
1955            return;
1956        }
1957
1958        AccessibilityEvent event =
1959            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1960        event.setPackageName(packageName);
1961        event.setClassName(Notification.class.getName());
1962        event.setParcelableData(notification);
1963        CharSequence tickerText = notification.tickerText;
1964        if (!TextUtils.isEmpty(tickerText)) {
1965            event.getText().add(tickerText);
1966        }
1967
1968        manager.sendAccessibilityEvent(event);
1969    }
1970
1971    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
1972        // tell the app
1973        if (sendDelete) {
1974            if (r.getNotification().deleteIntent != null) {
1975                try {
1976                    r.getNotification().deleteIntent.send();
1977                } catch (PendingIntent.CanceledException ex) {
1978                    // do nothing - there's no relevant way to recover, and
1979                    //     no reason to let this propagate
1980                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
1981                }
1982            }
1983        }
1984
1985        // status bar
1986        if (r.getNotification().icon != 0) {
1987            r.isCanceled = true;
1988            mListeners.notifyRemovedLocked(r.sbn);
1989        }
1990
1991        // sound
1992        if (mSoundNotification == r) {
1993            mSoundNotification = null;
1994            final long identity = Binder.clearCallingIdentity();
1995            try {
1996                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
1997                if (player != null) {
1998                    player.stopAsync();
1999                }
2000            } catch (RemoteException e) {
2001            } finally {
2002                Binder.restoreCallingIdentity(identity);
2003            }
2004        }
2005
2006        // vibrate
2007        if (mVibrateNotification == r) {
2008            mVibrateNotification = null;
2009            long identity = Binder.clearCallingIdentity();
2010            try {
2011                mVibrator.cancel();
2012            }
2013            finally {
2014                Binder.restoreCallingIdentity(identity);
2015            }
2016        }
2017
2018        // light
2019        mLights.remove(r.getKey());
2020        if (mLedNotification == r) {
2021            mLedNotification = null;
2022        }
2023
2024        // Record usage stats
2025        switch (reason) {
2026            case REASON_DELEGATE_CANCEL:
2027            case REASON_DELEGATE_CANCEL_ALL:
2028            case REASON_LISTENER_CANCEL:
2029            case REASON_LISTENER_CANCEL_ALL:
2030                mUsageStats.registerDismissedByUser(r);
2031                break;
2032            case REASON_NOMAN_CANCEL:
2033            case REASON_NOMAN_CANCEL_ALL:
2034                mUsageStats.registerRemovedByApp(r);
2035                break;
2036            case REASON_DELEGATE_CLICK:
2037                mUsageStats.registerCancelDueToClick(r);
2038                break;
2039            default:
2040                mUsageStats.registerCancelUnknown(r);
2041                break;
2042        }
2043
2044        // Save it for users of getHistoricalNotifications()
2045        mArchive.record(r.sbn);
2046    }
2047
2048    /**
2049     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2050     * and none of the {@code mustNotHaveFlags}.
2051     */
2052    void cancelNotification(final int callingUid, final int callingPid,
2053            final String pkg, final String tag, final int id,
2054            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2055            final int userId, final int reason, final ManagedServiceInfo listener) {
2056        // In enqueueNotificationInternal notifications are added by scheduling the
2057        // work on the worker handler. Hence, we also schedule the cancel on this
2058        // handler to avoid a scenario where an add notification call followed by a
2059        // remove notification call ends up in not removing the notification.
2060        mHandler.post(new Runnable() {
2061            @Override
2062            public void run() {
2063                String listenerName = listener == null ? null : listener.component.toShortString();
2064                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2065                        mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2066
2067                synchronized (mNotificationList) {
2068                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2069                    if (index >= 0) {
2070                        NotificationRecord r = mNotificationList.get(index);
2071
2072                        // Ideally we'd do this in the caller of this method. However, that would
2073                        // require the caller to also find the notification.
2074                        if (reason == REASON_DELEGATE_CLICK) {
2075                            mUsageStats.registerClickedByUser(r);
2076                        }
2077
2078                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2079                            return;
2080                        }
2081                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2082                            return;
2083                        }
2084
2085                        mNotificationList.remove(index);
2086                        mNotificationsByKey.remove(r.sbn.getKey());
2087
2088                        cancelNotificationLocked(r, sendDelete, reason);
2089                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName);
2090                        updateLightsLocked();
2091                    }
2092                }
2093            }
2094        });
2095    }
2096
2097    /**
2098     * Determine whether the userId applies to the notification in question, either because
2099     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2100     */
2101    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2102        return
2103                // looking for USER_ALL notifications? match everything
2104                   userId == UserHandle.USER_ALL
2105                // a notification sent to USER_ALL matches any query
2106                || r.getUserId() == UserHandle.USER_ALL
2107                // an exact user match
2108                || r.getUserId() == userId;
2109    }
2110
2111    /**
2112     * Determine whether the userId applies to the notification in question, either because
2113     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2114     * because it matches one of the users profiles.
2115     */
2116    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2117        return notificationMatchesUserId(r, userId)
2118                || mUserProfiles.isCurrentProfile(r.getUserId());
2119    }
2120
2121    /**
2122     * Cancels all notifications from a given package that have all of the
2123     * {@code mustHaveFlags}.
2124     */
2125    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2126            int mustNotHaveFlags, boolean doit, int userId, int reason,
2127            ManagedServiceInfo listener) {
2128        String listenerName = listener == null ? null : listener.component.toShortString();
2129        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2130                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2131                listenerName);
2132
2133        synchronized (mNotificationList) {
2134            final int N = mNotificationList.size();
2135            ArrayList<NotificationRecord> canceledNotifications = null;
2136            for (int i = N-1; i >= 0; --i) {
2137                NotificationRecord r = mNotificationList.get(i);
2138                if (!notificationMatchesUserId(r, userId)) {
2139                    continue;
2140                }
2141                // Don't remove notifications to all, if there's no package name specified
2142                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2143                    continue;
2144                }
2145                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2146                    continue;
2147                }
2148                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2149                    continue;
2150                }
2151                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2152                    continue;
2153                }
2154                if (canceledNotifications == null) {
2155                    canceledNotifications = new ArrayList<>();
2156                }
2157                canceledNotifications.add(r);
2158                if (!doit) {
2159                    return true;
2160                }
2161                mNotificationList.remove(i);
2162                mNotificationsByKey.remove(r.sbn.getKey());
2163                cancelNotificationLocked(r, false, reason);
2164            }
2165            if (doit && canceledNotifications != null) {
2166                final int M = canceledNotifications.size();
2167                for (int i = 0; i < M; i++) {
2168                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2169                            listenerName);
2170                }
2171            }
2172            if (canceledNotifications != null) {
2173                updateLightsLocked();
2174            }
2175            return canceledNotifications != null;
2176        }
2177    }
2178
2179    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2180            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2181        String listenerName = listener == null ? null : listener.component.toShortString();
2182        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2183                null, userId, 0, 0, reason, listenerName);
2184
2185        ArrayList<NotificationRecord> canceledNotifications = null;
2186        final int N = mNotificationList.size();
2187        for (int i=N-1; i>=0; i--) {
2188            NotificationRecord r = mNotificationList.get(i);
2189            if (includeCurrentProfiles) {
2190                if (!notificationMatchesCurrentProfiles(r, userId)) {
2191                    continue;
2192                }
2193            } else {
2194                if (!notificationMatchesUserId(r, userId)) {
2195                    continue;
2196                }
2197            }
2198
2199            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2200                            | Notification.FLAG_NO_CLEAR)) == 0) {
2201                mNotificationList.remove(i);
2202                mNotificationsByKey.remove(r.sbn.getKey());
2203                cancelNotificationLocked(r, true, reason);
2204                // Make a note so we can cancel children later.
2205                if (canceledNotifications == null) {
2206                    canceledNotifications = new ArrayList<>();
2207                }
2208                canceledNotifications.add(r);
2209            }
2210        }
2211        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
2212        for (int i = 0; i < M; i++) {
2213            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2214                    listenerName);
2215        }
2216        updateLightsLocked();
2217    }
2218
2219    // Warning: The caller is responsible for invoking updateLightsLocked().
2220    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
2221            String listenerName) {
2222        Notification n = r.getNotification();
2223        if (n.getGroup() == null || (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0) {
2224            return;
2225        }
2226
2227        String pkg = r.sbn.getPackageName();
2228        int userId = r.getUserId();
2229
2230        if (pkg == null) {
2231            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
2232            return;
2233        }
2234
2235        final int N = mNotificationList.size();
2236        for (int i = N - 1; i >= 0; i--) {
2237            NotificationRecord childR = mNotificationList.get(i);
2238            Notification childN = childR.getNotification();
2239            StatusBarNotification childSbn = childR.sbn;
2240            if (childR.getUserId() == userId && pkg.equals(childSbn.getPackageName()) &&
2241                    n.getGroup().equals(childN.getGroup())) {
2242                EventLogTags.writeNotificationCancel(callingUid, callingPid,
2243                        pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0,
2244                        REASON_GROUP_SUMMARY_CANCELED, listenerName);
2245                mNotificationList.remove(i);
2246                mNotificationsByKey.remove(childR.getKey());
2247                cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);
2248            }
2249        }
2250    }
2251
2252    // lock on mNotificationList
2253    void updateLightsLocked()
2254    {
2255        // handle notification lights
2256        if (mLedNotification == null) {
2257            // get next notification, if any
2258            int n = mLights.size();
2259            if (n > 0) {
2260                mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
2261            }
2262        }
2263
2264        // Don't flash while we are in a call or screen is on
2265        if (mLedNotification == null || mInCall || mScreenOn) {
2266            mNotificationLight.turnOff();
2267        } else {
2268            final Notification ledno = mLedNotification.sbn.getNotification();
2269            int ledARGB = ledno.ledARGB;
2270            int ledOnMS = ledno.ledOnMS;
2271            int ledOffMS = ledno.ledOffMS;
2272            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2273                ledARGB = mDefaultNotificationColor;
2274                ledOnMS = mDefaultNotificationLedOn;
2275                ledOffMS = mDefaultNotificationLedOff;
2276            }
2277            if (mNotificationPulseEnabled) {
2278                // pulse repeatedly
2279                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2280                        ledOnMS, ledOffMS);
2281            }
2282        }
2283    }
2284
2285    // lock on mNotificationList
2286    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2287    {
2288        ArrayList<NotificationRecord> list = mNotificationList;
2289        final int len = list.size();
2290        for (int i=0; i<len; i++) {
2291            NotificationRecord r = list.get(i);
2292            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2293                continue;
2294            }
2295            if (tag == null) {
2296                if (r.sbn.getTag() != null) {
2297                    continue;
2298                }
2299            } else {
2300                if (!tag.equals(r.sbn.getTag())) {
2301                    continue;
2302                }
2303            }
2304            if (r.sbn.getPackageName().equals(pkg)) {
2305                return i;
2306            }
2307        }
2308        return -1;
2309    }
2310
2311    // lock on mNotificationList
2312    int indexOfNotificationLocked(String key) {
2313        final int N = mNotificationList.size();
2314        for (int i = 0; i < N; i++) {
2315            if (key.equals(mNotificationList.get(i).getKey())) {
2316                return i;
2317            }
2318        }
2319        return -1;
2320    }
2321
2322    private void updateNotificationPulse() {
2323        synchronized (mNotificationList) {
2324            updateLightsLocked();
2325        }
2326    }
2327
2328    private static boolean isUidSystem(int uid) {
2329        final int appid = UserHandle.getAppId(uid);
2330        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2331    }
2332
2333    private static boolean isCallerSystem() {
2334        return isUidSystem(Binder.getCallingUid());
2335    }
2336
2337    private static void checkCallerIsSystem() {
2338        if (isCallerSystem()) {
2339            return;
2340        }
2341        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2342    }
2343
2344    private static void checkCallerIsSystemOrSameApp(String pkg) {
2345        if (isCallerSystem()) {
2346            return;
2347        }
2348        final int uid = Binder.getCallingUid();
2349        try {
2350            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2351                    pkg, 0, UserHandle.getCallingUserId());
2352            if (!UserHandle.isSameApp(ai.uid, uid)) {
2353                throw new SecurityException("Calling uid " + uid + " gave package"
2354                        + pkg + " which is owned by uid " + ai.uid);
2355            }
2356        } catch (RemoteException re) {
2357            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2358        }
2359    }
2360
2361    /**
2362     * Generates a NotificationRankingUpdate from 'sbns', considering only
2363     * notifications visible to the given listener.
2364     *
2365     * <p>Caller must hold a lock on mNotificationList.</p>
2366     */
2367    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
2368        int speedBumpIndex = -1;
2369        final int N = mNotificationList.size();
2370        ArrayList<String> keys = new ArrayList<String>(N);
2371        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
2372        for (int i = 0; i < N; i++) {
2373            NotificationRecord record = mNotificationList.get(i);
2374            if (!info.enabledAndUserMatches(record.sbn.getUserId())) {
2375                continue;
2376            }
2377            keys.add(record.sbn.getKey());
2378            if (record.isIntercepted()) {
2379                interceptedKeys.add(record.sbn.getKey());
2380            }
2381            if (speedBumpIndex == -1 &&
2382                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
2383                speedBumpIndex = keys.size() - 1;
2384            }
2385        }
2386        String[] keysAr = keys.toArray(new String[keys.size()]);
2387        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
2388        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, speedBumpIndex);
2389    }
2390
2391    public class NotificationListeners extends ManagedServices {
2392
2393        public NotificationListeners() {
2394            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2395        }
2396
2397        @Override
2398        protected Config getConfig() {
2399            Config c = new Config();
2400            c.caption = "notification listener";
2401            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2402            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2403            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2404            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2405            c.clientLabel = R.string.notification_listener_binding_label;
2406            return c;
2407        }
2408
2409        @Override
2410        protected IInterface asInterface(IBinder binder) {
2411            return INotificationListener.Stub.asInterface(binder);
2412        }
2413
2414        @Override
2415        public void onServiceAdded(ManagedServiceInfo info) {
2416            final INotificationListener listener = (INotificationListener) info.service;
2417            final NotificationRankingUpdate update;
2418            synchronized (mNotificationList) {
2419                update = makeRankingUpdateLocked(info);
2420            }
2421            try {
2422                listener.onListenerConnected(update);
2423            } catch (RemoteException e) {
2424                // we tried
2425            }
2426        }
2427
2428        /**
2429         * asynchronously notify all listeners about a new notification
2430         */
2431        public void notifyPostedLocked(StatusBarNotification sbn) {
2432            // make a copy in case changes are made to the underlying Notification object
2433            final StatusBarNotification sbnClone = sbn.clone();
2434            for (final ManagedServiceInfo info : mServices) {
2435                if (!info.isEnabledForCurrentProfiles()) {
2436                    continue;
2437                }
2438                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2439                if (update.getOrderedKeys().length == 0) {
2440                    continue;
2441                }
2442                mHandler.post(new Runnable() {
2443                    @Override
2444                    public void run() {
2445                        notifyPostedIfUserMatch(info, sbnClone, update);
2446                    }
2447                });
2448            }
2449        }
2450
2451        /**
2452         * asynchronously notify all listeners about a removed notification
2453         */
2454        public void notifyRemovedLocked(StatusBarNotification sbn) {
2455            // make a copy in case changes are made to the underlying Notification object
2456            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2457            // notification
2458            final StatusBarNotification sbnLight = sbn.cloneLight();
2459            for (final ManagedServiceInfo info : mServices) {
2460                if (!info.isEnabledForCurrentProfiles()) {
2461                    continue;
2462                }
2463                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
2464                mHandler.post(new Runnable() {
2465                    @Override
2466                    public void run() {
2467                        notifyRemovedIfUserMatch(info, sbnLight, update);
2468                    }
2469                });
2470            }
2471        }
2472
2473        /**
2474         * asynchronously notify all listeners about a reordering of notifications
2475         */
2476        public void notifyRankingUpdateLocked() {
2477            for (final ManagedServiceInfo serviceInfo : mServices) {
2478                if (!serviceInfo.isEnabledForCurrentProfiles()) {
2479                    continue;
2480                }
2481                final NotificationRankingUpdate update =
2482                        makeRankingUpdateLocked(serviceInfo);
2483                mHandler.post(new Runnable() {
2484                    @Override
2485                    public void run() {
2486                        notifyRankingUpdate(serviceInfo, update);
2487                    }
2488                });
2489            }
2490        }
2491
2492        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
2493                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
2494            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2495                return;
2496            }
2497            final INotificationListener listener = (INotificationListener)info.service;
2498            try {
2499                listener.onNotificationPosted(sbn, rankingUpdate);
2500            } catch (RemoteException ex) {
2501                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2502            }
2503        }
2504
2505        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
2506                NotificationRankingUpdate rankingUpdate) {
2507            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2508                return;
2509            }
2510            final INotificationListener listener = (INotificationListener) info.service;
2511            try {
2512                listener.onNotificationRemoved(sbn, rankingUpdate);
2513            } catch (RemoteException ex) {
2514                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2515            }
2516        }
2517
2518        private void notifyRankingUpdate(ManagedServiceInfo info,
2519                                         NotificationRankingUpdate rankingUpdate) {
2520            final INotificationListener listener = (INotificationListener) info.service;
2521            try {
2522                listener.onNotificationRankingUpdate(rankingUpdate);
2523            } catch (RemoteException ex) {
2524                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2525            }
2526        }
2527    }
2528
2529    public static final class DumpFilter {
2530        public String pkgFilter;
2531
2532        public static DumpFilter parseFromArguments(String[] args) {
2533            if (args == null || args.length != 2 || !"p".equals(args[0])
2534                    || args[1] == null || args[1].trim().isEmpty()) {
2535                return null;
2536            }
2537            final DumpFilter filter = new DumpFilter();
2538            filter.pkgFilter = args[1].trim().toLowerCase();
2539            return filter;
2540        }
2541
2542        public boolean matches(StatusBarNotification sbn) {
2543            return sbn != null && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
2544        }
2545
2546        public boolean matches(ComponentName component) {
2547            return component != null && matches(component.getPackageName());
2548        }
2549
2550        public boolean matches(String pkg) {
2551            return pkg != null && pkg.toLowerCase().contains(pkgFilter);
2552        }
2553    }
2554}
2555