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