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