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