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