NotificationManagerService.java revision af8d6c44f06d2f8baac2c5774a9efdae3fc36797
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        SingleNotificationStats stats;
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            final long identity = Binder.clearCallingIdentity();
1405            try {
1406                mConditionProviders.setZenModeCondition(conditionId);
1407            } finally {
1408                Binder.restoreCallingIdentity(identity);
1409            }
1410        }
1411
1412        @Override
1413        public void setAutomaticZenModeConditions(Uri[] conditionIds) {
1414            enforceSystemOrSystemUI("INotificationManager.setAutomaticZenModeConditions");
1415            mConditionProviders.setAutomaticZenModeConditions(conditionIds);
1416        }
1417
1418        @Override
1419        public Condition[] getAutomaticZenModeConditions() {
1420            enforceSystemOrSystemUI("INotificationManager.getAutomaticZenModeConditions");
1421            return mConditionProviders.getAutomaticZenModeConditions();
1422        }
1423
1424        private void enforceSystemOrSystemUI(String message) {
1425            if (isCallerSystem()) return;
1426            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1427                    message);
1428        }
1429
1430        @Override
1431        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1432            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1433                    != PackageManager.PERMISSION_GRANTED) {
1434                pw.println("Permission Denial: can't dump NotificationManager from from pid="
1435                        + Binder.getCallingPid()
1436                        + ", uid=" + Binder.getCallingUid());
1437                return;
1438            }
1439
1440            dumpImpl(pw);
1441        }
1442    };
1443
1444    private String[] getActiveNotificationKeys(INotificationListener token) {
1445        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1446        final ArrayList<String> keys = new ArrayList<String>();
1447        if (info.isEnabledForCurrentProfiles()) {
1448            synchronized (mNotificationList) {
1449                final int N = mNotificationList.size();
1450                for (int i = 0; i < N; i++) {
1451                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1452                    if (info.enabledAndUserMatches(sbn.getUserId())) {
1453                        keys.add(sbn.getKey());
1454                    }
1455                }
1456            }
1457        }
1458        return keys.toArray(new String[keys.size()]);
1459    }
1460
1461    void dumpImpl(PrintWriter pw) {
1462        pw.println("Current Notification Manager state:");
1463
1464        int N;
1465
1466        synchronized (mToastQueue) {
1467            N = mToastQueue.size();
1468            if (N > 0) {
1469                pw.println("  Toast Queue:");
1470                for (int i=0; i<N; i++) {
1471                    mToastQueue.get(i).dump(pw, "    ");
1472                }
1473                pw.println("  ");
1474            }
1475
1476        }
1477
1478        synchronized (mNotificationList) {
1479            N = mNotificationList.size();
1480            if (N > 0) {
1481                pw.println("  Notification List:");
1482                for (int i=0; i<N; i++) {
1483                    mNotificationList.get(i).dump(pw, "    ", getContext());
1484                }
1485                pw.println("  ");
1486            }
1487
1488            N = mLights.size();
1489            if (N > 0) {
1490                pw.println("  Lights List:");
1491                for (int i=0; i<N; i++) {
1492                    pw.println("    " + mLights.get(i));
1493                }
1494                pw.println("  ");
1495            }
1496
1497            pw.println("  mSoundNotification=" + mSoundNotification);
1498            pw.println("  mVibrateNotification=" + mVibrateNotification);
1499            pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
1500            pw.println("  mSystemReady=" + mSystemReady);
1501            pw.println("  mArchive=" + mArchive.toString());
1502            Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1503            int i=0;
1504            while (iter.hasNext()) {
1505                pw.println("    " + iter.next());
1506                if (++i >= 5) {
1507                    if (iter.hasNext()) pw.println("    ...");
1508                    break;
1509                }
1510            }
1511
1512            pw.println("\n  Usage Stats:");
1513            mUsageStats.dump(pw, "    ");
1514
1515            pw.println("\n  Zen Mode:");
1516            mZenModeHelper.dump(pw, "    ");
1517
1518            pw.println("\n  Notification listeners:");
1519            mListeners.dump(pw);
1520
1521            pw.println("\n  Condition providers:");
1522            mConditionProviders.dump(pw);
1523        }
1524    }
1525
1526    /**
1527     * The private API only accessible to the system process.
1528     */
1529    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
1530        @Override
1531        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
1532                String tag, int id, Notification notification, int[] idReceived, int userId) {
1533            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
1534                    idReceived, userId);
1535        }
1536    };
1537
1538    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
1539            final int callingPid, final String tag, final int id, final Notification notification,
1540            int[] idOut, int incomingUserId) {
1541        if (DBG) {
1542            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
1543                    + " notification=" + notification);
1544        }
1545        checkCallerIsSystemOrSameApp(pkg);
1546        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
1547
1548        final int userId = ActivityManager.handleIncomingUser(callingPid,
1549                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
1550        final UserHandle user = new UserHandle(userId);
1551
1552        // Limit the number of notifications that any given package except the android
1553        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1554        if (!isSystemNotification) {
1555            synchronized (mNotificationList) {
1556                int count = 0;
1557                final int N = mNotificationList.size();
1558                for (int i=0; i<N; i++) {
1559                    final NotificationRecord r = mNotificationList.get(i);
1560                    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
1561                        count++;
1562                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1563                            Slog.e(TAG, "Package has already posted " + count
1564                                    + " notifications.  Not showing more.  package=" + pkg);
1565                            return;
1566                        }
1567                    }
1568                }
1569            }
1570        }
1571
1572        // This conditional is a dirty hack to limit the logging done on
1573        //     behalf of the download manager without affecting other apps.
1574        if (!pkg.equals("com.android.providers.downloads")
1575                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
1576            EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
1577                    pkg, id, tag, userId, notification.toString());
1578        }
1579
1580        if (pkg == null || notification == null) {
1581            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
1582                    + " id=" + id + " notification=" + notification);
1583        }
1584        if (notification.icon != 0) {
1585            if (notification.contentView == null) {
1586                throw new IllegalArgumentException("contentView required: pkg=" + pkg
1587                        + " id=" + id + " notification=" + notification);
1588            }
1589        }
1590
1591        mHandler.post(new Runnable() {
1592            @Override
1593            public void run() {
1594
1595                // === Scoring ===
1596
1597                // 0. Sanitize inputs
1598                notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
1599                        Notification.PRIORITY_MAX);
1600                // Migrate notification flags to scores
1601                if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
1602                    if (notification.priority < Notification.PRIORITY_MAX) {
1603                        notification.priority = Notification.PRIORITY_MAX;
1604                    }
1605                } else if (SCORE_ONGOING_HIGHER &&
1606                        0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
1607                    if (notification.priority < Notification.PRIORITY_HIGH) {
1608                        notification.priority = Notification.PRIORITY_HIGH;
1609                    }
1610                }
1611
1612                // 1. initial score: buckets of 10, around the app
1613                int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
1614
1615                // 2. extract ranking signals from the notification data
1616                final StatusBarNotification n = new StatusBarNotification(
1617                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
1618                        user);
1619                NotificationRecord r = new NotificationRecord(n);
1620                if (!mSignalExtractors.isEmpty()) {
1621                    for (NotificationSignalExtractor extractor : mSignalExtractors) {
1622                        try {
1623                            RankingFuture future = extractor.process(r);
1624                            scheduleRankingReconsideration(future);
1625                        } catch (Throwable t) {
1626                            Slog.w(TAG, "NotificationSignalExtractor failed.", t);
1627                        }
1628                    }
1629                }
1630
1631                // 3. Apply local rules
1632
1633                // blocked apps
1634                if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
1635                    if (!isSystemNotification) {
1636                        score = JUNK_SCORE;
1637                        Slog.e(TAG, "Suppressing notification from package " + pkg
1638                                + " by user request.");
1639                    }
1640                }
1641
1642                if (score < SCORE_DISPLAY_THRESHOLD) {
1643                    // Notification will be blocked because the score is too low.
1644                    return;
1645                }
1646
1647                // Is this notification intercepted by zen mode?
1648                final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
1649                notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
1650
1651                // Should this notification make noise, vibe, or use the LED?
1652                final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
1653                if (DBG || intercept) Slog.v(TAG,
1654                        "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
1655                synchronized (mNotificationList) {
1656                    NotificationRecord old = null;
1657                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
1658                    if (index < 0) {
1659                        mNotificationList.add(r);
1660                        mUsageStats.registerPostedByApp(r);
1661                    } else {
1662                        old = mNotificationList.get(index);
1663                        mNotificationList.set(index, r);
1664                        mUsageStats.registerUpdatedByApp(r, old);
1665                        // Make sure we don't lose the foreground service state.
1666                        if (old != null) {
1667                            notification.flags |=
1668                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
1669                        }
1670                    }
1671                    if (old != null) {
1672                        mNotificationsByKey.remove(old.sbn.getKey());
1673                    }
1674                    mNotificationsByKey.put(n.getKey(), r);
1675
1676                    Collections.sort(mNotificationList, mRankingComparator);
1677
1678                    // Ensure if this is a foreground service that the proper additional
1679                    // flags are set.
1680                    if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
1681                        notification.flags |= Notification.FLAG_ONGOING_EVENT
1682                                | Notification.FLAG_NO_CLEAR;
1683                    }
1684
1685                    final int currentUser;
1686                    final long token = Binder.clearCallingIdentity();
1687                    try {
1688                        currentUser = ActivityManager.getCurrentUser();
1689                    } finally {
1690                        Binder.restoreCallingIdentity(token);
1691                    }
1692
1693                    if (notification.icon != 0) {
1694                        if (old != null && old.statusBarKey != null) {
1695                            r.statusBarKey = old.statusBarKey;
1696                            final long identity = Binder.clearCallingIdentity();
1697                            try {
1698                                mStatusBar.updateNotification(r.statusBarKey, n);
1699                            } finally {
1700                                Binder.restoreCallingIdentity(identity);
1701                            }
1702                        } else {
1703                            final long identity = Binder.clearCallingIdentity();
1704                            try {
1705                                r.statusBarKey = mStatusBar.addNotification(n);
1706                                if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
1707                                        && canInterrupt) {
1708                                    mAttentionLight.pulse();
1709                                }
1710                            } finally {
1711                                Binder.restoreCallingIdentity(identity);
1712                            }
1713                        }
1714                        // Send accessibility events only for the current user.
1715                        if (currentUser == userId) {
1716                            sendAccessibilityEvent(notification, pkg);
1717                        }
1718
1719                        mListeners.notifyPostedLocked(r.sbn);
1720                    } else {
1721                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
1722                        if (old != null && old.statusBarKey != null) {
1723                            final long identity = Binder.clearCallingIdentity();
1724                            try {
1725                                mStatusBar.removeNotification(old.statusBarKey);
1726                            } finally {
1727                                Binder.restoreCallingIdentity(identity);
1728                            }
1729
1730                            mListeners.notifyRemovedLocked(r.sbn);
1731                        }
1732                        // ATTENTION: in a future release we will bail out here
1733                        // so that we do not play sounds, show lights, etc. for invalid
1734                        // notifications
1735                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
1736                                + n.getPackageName());
1737                    }
1738
1739                    // If we're not supposed to beep, vibrate, etc. then don't.
1740                    if (!mDisableNotificationAlerts
1741                            && (!(old != null
1742                                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
1743                            && (r.getUserId() == UserHandle.USER_ALL ||
1744                                (r.getUserId() == userId && r.getUserId() == currentUser) ||
1745                                mUserProfiles.isCurrentProfile(r.getUserId()))
1746                            && canInterrupt
1747                            && mSystemReady
1748                            && mAudioManager != null) {
1749                        if (DBG) Slog.v(TAG, "Interrupting!");
1750                        // sound
1751
1752                        // should we use the default notification sound? (indicated either by
1753                        // DEFAULT_SOUND or because notification.sound is pointing at
1754                        // Settings.System.NOTIFICATION_SOUND)
1755                        final boolean useDefaultSound =
1756                               (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
1757                                       Settings.System.DEFAULT_NOTIFICATION_URI
1758                                               .equals(notification.sound);
1759
1760                        Uri soundUri = null;
1761                        boolean hasValidSound = false;
1762
1763                        if (useDefaultSound) {
1764                            soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
1765
1766                            // check to see if the default notification sound is silent
1767                            ContentResolver resolver = getContext().getContentResolver();
1768                            hasValidSound = Settings.System.getString(resolver,
1769                                   Settings.System.NOTIFICATION_SOUND) != null;
1770                        } else if (notification.sound != null) {
1771                            soundUri = notification.sound;
1772                            hasValidSound = (soundUri != null);
1773                        }
1774
1775                        if (hasValidSound) {
1776                            boolean looping =
1777                                    (notification.flags & Notification.FLAG_INSISTENT) != 0;
1778                            int audioStreamType;
1779                            if (notification.audioStreamType >= 0) {
1780                                audioStreamType = notification.audioStreamType;
1781                            } else {
1782                                audioStreamType = DEFAULT_STREAM_TYPE;
1783                            }
1784                            mSoundNotification = r;
1785                            // do not play notifications if stream volume is 0 (typically because
1786                            // ringer mode is silent) or if there is a user of exclusive audio focus
1787                            if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
1788                                    && !mAudioManager.isAudioFocusExclusive()) {
1789                                final long identity = Binder.clearCallingIdentity();
1790                                try {
1791                                    final IRingtonePlayer player =
1792                                            mAudioManager.getRingtonePlayer();
1793                                    if (player != null) {
1794                                        if (DBG) Slog.v(TAG, "Playing sound " + soundUri
1795                                                + " on stream " + audioStreamType);
1796                                        player.playAsync(soundUri, user, looping, audioStreamType);
1797                                    }
1798                                } catch (RemoteException e) {
1799                                } finally {
1800                                    Binder.restoreCallingIdentity(identity);
1801                                }
1802                            }
1803                        }
1804
1805                        // vibrate
1806                        // Does the notification want to specify its own vibration?
1807                        final boolean hasCustomVibrate = notification.vibrate != null;
1808
1809                        // new in 4.2: if there was supposed to be a sound and we're in vibrate
1810                        // mode, and no other vibration is specified, we fall back to vibration
1811                        final boolean convertSoundToVibration =
1812                                   !hasCustomVibrate
1813                                && hasValidSound
1814                                && (mAudioManager.getRingerMode()
1815                                           == AudioManager.RINGER_MODE_VIBRATE);
1816
1817                        // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
1818                        final boolean useDefaultVibrate =
1819                                (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
1820
1821                        if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
1822                                && !(mAudioManager.getRingerMode()
1823                                        == AudioManager.RINGER_MODE_SILENT)) {
1824                            mVibrateNotification = r;
1825
1826                            if (useDefaultVibrate || convertSoundToVibration) {
1827                                // Escalate privileges so we can use the vibrator even if the
1828                                // notifying app does not have the VIBRATE permission.
1829                                long identity = Binder.clearCallingIdentity();
1830                                try {
1831                                    mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
1832                                        useDefaultVibrate ? mDefaultVibrationPattern
1833                                            : mFallbackVibrationPattern,
1834                                        ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1835                                                ? 0: -1, notification.audioStreamType);
1836                                } finally {
1837                                    Binder.restoreCallingIdentity(identity);
1838                                }
1839                            } else if (notification.vibrate.length > 1) {
1840                                // If you want your own vibration pattern, you need the VIBRATE
1841                                // permission
1842                                mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
1843                                        notification.vibrate,
1844                                    ((notification.flags & Notification.FLAG_INSISTENT) != 0)
1845                                            ? 0: -1, notification.audioStreamType);
1846                            }
1847                        }
1848                    }
1849
1850                    // light
1851                    // the most recent thing gets the light
1852                    mLights.remove(old);
1853                    if (mLedNotification == old) {
1854                        mLedNotification = null;
1855                    }
1856                    //Slog.i(TAG, "notification.lights="
1857                    //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
1858                    //                  != 0));
1859                    if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
1860                            && canInterrupt) {
1861                        mLights.add(r);
1862                        updateLightsLocked();
1863                    } else {
1864                        if (old != null
1865                                && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
1866                            updateLightsLocked();
1867                        }
1868                    }
1869                }
1870            }
1871        });
1872
1873        idOut[0] = id;
1874    }
1875
1876    void showNextToastLocked() {
1877        ToastRecord record = mToastQueue.get(0);
1878        while (record != null) {
1879            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
1880            try {
1881                record.callback.show();
1882                scheduleTimeoutLocked(record);
1883                return;
1884            } catch (RemoteException e) {
1885                Slog.w(TAG, "Object died trying to show notification " + record.callback
1886                        + " in package " + record.pkg);
1887                // remove it from the list and let the process die
1888                int index = mToastQueue.indexOf(record);
1889                if (index >= 0) {
1890                    mToastQueue.remove(index);
1891                }
1892                keepProcessAliveLocked(record.pid);
1893                if (mToastQueue.size() > 0) {
1894                    record = mToastQueue.get(0);
1895                } else {
1896                    record = null;
1897                }
1898            }
1899        }
1900    }
1901
1902    void cancelToastLocked(int index) {
1903        ToastRecord record = mToastQueue.get(index);
1904        try {
1905            record.callback.hide();
1906        } catch (RemoteException e) {
1907            Slog.w(TAG, "Object died trying to hide notification " + record.callback
1908                    + " in package " + record.pkg);
1909            // don't worry about this, we're about to remove it from
1910            // the list anyway
1911        }
1912        mToastQueue.remove(index);
1913        keepProcessAliveLocked(record.pid);
1914        if (mToastQueue.size() > 0) {
1915            // Show the next one. If the callback fails, this will remove
1916            // it from the list, so don't assume that the list hasn't changed
1917            // after this point.
1918            showNextToastLocked();
1919        }
1920    }
1921
1922    private void scheduleTimeoutLocked(ToastRecord r)
1923    {
1924        mHandler.removeCallbacksAndMessages(r);
1925        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
1926        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
1927        mHandler.sendMessageDelayed(m, delay);
1928    }
1929
1930    private void handleTimeout(ToastRecord record)
1931    {
1932        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
1933        synchronized (mToastQueue) {
1934            int index = indexOfToastLocked(record.pkg, record.callback);
1935            if (index >= 0) {
1936                cancelToastLocked(index);
1937            }
1938        }
1939    }
1940
1941    // lock on mToastQueue
1942    int indexOfToastLocked(String pkg, ITransientNotification callback)
1943    {
1944        IBinder cbak = callback.asBinder();
1945        ArrayList<ToastRecord> list = mToastQueue;
1946        int len = list.size();
1947        for (int i=0; i<len; i++) {
1948            ToastRecord r = list.get(i);
1949            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
1950                return i;
1951            }
1952        }
1953        return -1;
1954    }
1955
1956    // lock on mToastQueue
1957    void keepProcessAliveLocked(int pid)
1958    {
1959        int toastCount = 0; // toasts from this pid
1960        ArrayList<ToastRecord> list = mToastQueue;
1961        int N = list.size();
1962        for (int i=0; i<N; i++) {
1963            ToastRecord r = list.get(i);
1964            if (r.pid == pid) {
1965                toastCount++;
1966            }
1967        }
1968        try {
1969            mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
1970        } catch (RemoteException e) {
1971            // Shouldn't happen.
1972        }
1973    }
1974
1975    private void scheduleRankingReconsideration(RankingFuture future) {
1976        if (future != null) {
1977            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
1978            long delay = future.getDelay(TimeUnit.MILLISECONDS);
1979            mRankingHandler.sendMessageDelayed(m, delay);
1980        }
1981    }
1982
1983    private void handleRankingReconsideration(Message message) {
1984        if (!(message.obj instanceof RankingFuture)) return;
1985
1986        RankingFuture future = (RankingFuture) message.obj;
1987        future.run();
1988        try {
1989            NotificationRecord record = future.get();
1990            synchronized (mNotificationList) {
1991                int before = mNotificationList.indexOf(record);
1992                if (before != -1) {
1993                    Collections.sort(mNotificationList, mRankingComparator);
1994                    int after = mNotificationList.indexOf(record);
1995
1996                    if (before != after) {
1997                        scheduleSendRankingUpdate();
1998                    }
1999                }
2000            }
2001        } catch (InterruptedException e) {
2002            // we're running the future explicitly, so this should never happen
2003        } catch (ExecutionException e) {
2004            // we're running the future explicitly, so this should never happen
2005        }
2006    }
2007
2008    private void scheduleSendRankingUpdate() {
2009        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2010        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2011        mHandler.sendMessage(m);
2012    }
2013
2014    private void handleSendRankingUpdate() {
2015        synchronized (mNotificationList) {
2016            final int N = mNotificationList.size();
2017            ArrayList<StatusBarNotification> sbns =
2018                    new ArrayList<StatusBarNotification>(N);
2019            for (int i = 0; i < N; i++ ) {
2020                sbns.add(mNotificationList.get(i).sbn);
2021            }
2022            mListeners.notifyOrderUpdateLocked(sbns);
2023        }
2024    }
2025
2026    private final class WorkerHandler extends Handler
2027    {
2028        @Override
2029        public void handleMessage(Message msg)
2030        {
2031            switch (msg.what)
2032            {
2033                case MESSAGE_TIMEOUT:
2034                    handleTimeout((ToastRecord)msg.obj);
2035                    break;
2036                case MESSAGE_SAVE_POLICY_FILE:
2037                    handleSavePolicyFile();
2038                    break;
2039                case MESSAGE_SEND_RANKING_UPDATE:
2040                    handleSendRankingUpdate();
2041                    break;
2042            }
2043        }
2044
2045    }
2046
2047    private final class RankingWorkerHandler extends Handler
2048    {
2049        public RankingWorkerHandler(Looper looper) {
2050            super(looper);
2051        }
2052
2053        @Override
2054        public void handleMessage(Message msg) {
2055            switch (msg.what) {
2056                case MESSAGE_RECONSIDER_RANKING:
2057                    handleRankingReconsideration(msg);
2058                    break;
2059            }
2060        }
2061    }
2062
2063    // Notifications
2064    // ============================================================================
2065    static int clamp(int x, int low, int high) {
2066        return (x < low) ? low : ((x > high) ? high : x);
2067    }
2068
2069    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2070        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2071        if (!manager.isEnabled()) {
2072            return;
2073        }
2074
2075        AccessibilityEvent event =
2076            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2077        event.setPackageName(packageName);
2078        event.setClassName(Notification.class.getName());
2079        event.setParcelableData(notification);
2080        CharSequence tickerText = notification.tickerText;
2081        if (!TextUtils.isEmpty(tickerText)) {
2082            event.getText().add(tickerText);
2083        }
2084
2085        manager.sendAccessibilityEvent(event);
2086    }
2087
2088    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2089        // tell the app
2090        if (sendDelete) {
2091            if (r.getNotification().deleteIntent != null) {
2092                try {
2093                    r.getNotification().deleteIntent.send();
2094                } catch (PendingIntent.CanceledException ex) {
2095                    // do nothing - there's no relevant way to recover, and
2096                    //     no reason to let this propagate
2097                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2098                }
2099            }
2100        }
2101
2102        // status bar
2103        if (r.getNotification().icon != 0) {
2104            final long identity = Binder.clearCallingIdentity();
2105            try {
2106                mStatusBar.removeNotification(r.statusBarKey);
2107            } finally {
2108                Binder.restoreCallingIdentity(identity);
2109            }
2110            r.statusBarKey = null;
2111            mListeners.notifyRemovedLocked(r.sbn);
2112        }
2113
2114        // sound
2115        if (mSoundNotification == r) {
2116            mSoundNotification = null;
2117            final long identity = Binder.clearCallingIdentity();
2118            try {
2119                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2120                if (player != null) {
2121                    player.stopAsync();
2122                }
2123            } catch (RemoteException e) {
2124            } finally {
2125                Binder.restoreCallingIdentity(identity);
2126            }
2127        }
2128
2129        // vibrate
2130        if (mVibrateNotification == r) {
2131            mVibrateNotification = null;
2132            long identity = Binder.clearCallingIdentity();
2133            try {
2134                mVibrator.cancel();
2135            }
2136            finally {
2137                Binder.restoreCallingIdentity(identity);
2138            }
2139        }
2140
2141        // light
2142        mLights.remove(r);
2143        if (mLedNotification == r) {
2144            mLedNotification = null;
2145        }
2146
2147        // Record usage stats
2148        switch (reason) {
2149            case REASON_DELEGATE_CANCEL:
2150            case REASON_DELEGATE_CANCEL_ALL:
2151            case REASON_LISTENER_CANCEL:
2152            case REASON_LISTENER_CANCEL_ALL:
2153                mUsageStats.registerDismissedByUser(r);
2154                break;
2155            case REASON_NOMAN_CANCEL:
2156            case REASON_NOMAN_CANCEL_ALL:
2157                mUsageStats.registerRemovedByApp(r);
2158                break;
2159            case REASON_DELEGATE_CLICK:
2160                mUsageStats.registerCancelDueToClick(r);
2161                break;
2162            default:
2163                mUsageStats.registerCancelUnknown(r);
2164                break;
2165        }
2166
2167        // Save it for users of getHistoricalNotifications()
2168        mArchive.record(r.sbn);
2169    }
2170
2171    /**
2172     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2173     * and none of the {@code mustNotHaveFlags}.
2174     */
2175    void cancelNotification(final int callingUid, final int callingPid,
2176            final String pkg, final String tag, final int id,
2177            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2178            final int userId, final int reason, final ManagedServiceInfo listener) {
2179        // In enqueueNotificationInternal notifications are added by scheduling the
2180        // work on the worker handler. Hence, we also schedule the cancel on this
2181        // handler to avoid a scenario where an add notification call followed by a
2182        // remove notification call ends up in not removing the notification.
2183        mHandler.post(new Runnable() {
2184            @Override
2185            public void run() {
2186                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag, userId,
2187                        mustHaveFlags, mustNotHaveFlags, reason,
2188                        listener == null ? null : listener.component.toShortString());
2189
2190                synchronized (mNotificationList) {
2191                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
2192                    if (index >= 0) {
2193                        NotificationRecord r = mNotificationList.get(index);
2194
2195                        // Ideally we'd do this in the caller of this method. However, that would
2196                        // require the caller to also find the notification.
2197                        if (reason == REASON_DELEGATE_CLICK) {
2198                            mUsageStats.registerClickedByUser(r);
2199                        }
2200
2201                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2202                            return;
2203                        }
2204                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2205                            return;
2206                        }
2207
2208                        mNotificationList.remove(index);
2209                        mNotificationsByKey.remove(r.sbn.getKey());
2210
2211                        cancelNotificationLocked(r, sendDelete, reason);
2212                        updateLightsLocked();
2213                    }
2214                }
2215            }
2216        });
2217    }
2218
2219    /**
2220     * Determine whether the userId applies to the notification in question, either because
2221     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2222     */
2223    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2224        return
2225                // looking for USER_ALL notifications? match everything
2226                   userId == UserHandle.USER_ALL
2227                // a notification sent to USER_ALL matches any query
2228                || r.getUserId() == UserHandle.USER_ALL
2229                // an exact user match
2230                || r.getUserId() == userId;
2231    }
2232
2233    /**
2234     * Determine whether the userId applies to the notification in question, either because
2235     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2236     * because it matches one of the users profiles.
2237     */
2238    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2239        return notificationMatchesUserId(r, userId)
2240                || mUserProfiles.isCurrentProfile(r.getUserId());
2241    }
2242
2243    /**
2244     * Cancels all notifications from a given package that have all of the
2245     * {@code mustHaveFlags}.
2246     */
2247    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2248            int mustNotHaveFlags, boolean doit, int userId, int reason,
2249            ManagedServiceInfo listener) {
2250        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2251                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2252                listener == null ? null : listener.component.toShortString());
2253
2254        synchronized (mNotificationList) {
2255            final int N = mNotificationList.size();
2256            boolean canceledSomething = false;
2257            for (int i = N-1; i >= 0; --i) {
2258                NotificationRecord r = mNotificationList.get(i);
2259                if (!notificationMatchesUserId(r, userId)) {
2260                    continue;
2261                }
2262                // Don't remove notifications to all, if there's no package name specified
2263                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2264                    continue;
2265                }
2266                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2267                    continue;
2268                }
2269                if ((r.getFlags() & mustNotHaveFlags) != 0) {
2270                    continue;
2271                }
2272                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2273                    continue;
2274                }
2275                canceledSomething = true;
2276                if (!doit) {
2277                    return true;
2278                }
2279                mNotificationList.remove(i);
2280                mNotificationsByKey.remove(r.sbn.getKey());
2281                cancelNotificationLocked(r, false, reason);
2282            }
2283            if (canceledSomething) {
2284                updateLightsLocked();
2285            }
2286            return canceledSomething;
2287        }
2288    }
2289
2290    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2291            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2292        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2293                null, userId, 0, 0, reason,
2294                listener == null ? null : listener.component.toShortString());
2295
2296        final int N = mNotificationList.size();
2297        for (int i=N-1; i>=0; i--) {
2298            NotificationRecord r = mNotificationList.get(i);
2299            if (includeCurrentProfiles) {
2300                if (!notificationMatchesCurrentProfiles(r, userId)) {
2301                    continue;
2302                }
2303            } else {
2304                if (!notificationMatchesUserId(r, userId)) {
2305                    continue;
2306                }
2307            }
2308
2309            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2310                            | Notification.FLAG_NO_CLEAR)) == 0) {
2311                mNotificationList.remove(i);
2312                mNotificationsByKey.remove(r.sbn.getKey());
2313                cancelNotificationLocked(r, true, reason);
2314            }
2315        }
2316        updateLightsLocked();
2317    }
2318
2319    // lock on mNotificationList
2320    void updateLightsLocked()
2321    {
2322        // handle notification lights
2323        if (mLedNotification == null) {
2324            // get next notification, if any
2325            int n = mLights.size();
2326            if (n > 0) {
2327                mLedNotification = mLights.get(n-1);
2328            }
2329        }
2330
2331        // Don't flash while we are in a call or screen is on
2332        if (mLedNotification == null || mInCall || mScreenOn) {
2333            mNotificationLight.turnOff();
2334        } else {
2335            final Notification ledno = mLedNotification.sbn.getNotification();
2336            int ledARGB = ledno.ledARGB;
2337            int ledOnMS = ledno.ledOnMS;
2338            int ledOffMS = ledno.ledOffMS;
2339            if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
2340                ledARGB = mDefaultNotificationColor;
2341                ledOnMS = mDefaultNotificationLedOn;
2342                ledOffMS = mDefaultNotificationLedOff;
2343            }
2344            if (mNotificationPulseEnabled) {
2345                // pulse repeatedly
2346                mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
2347                        ledOnMS, ledOffMS);
2348            }
2349        }
2350    }
2351
2352    // lock on mNotificationList
2353    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
2354    {
2355        ArrayList<NotificationRecord> list = mNotificationList;
2356        final int len = list.size();
2357        for (int i=0; i<len; i++) {
2358            NotificationRecord r = list.get(i);
2359            if (!notificationMatchesUserId(r, userId) || r.sbn.getId() != id) {
2360                continue;
2361            }
2362            if (tag == null) {
2363                if (r.sbn.getTag() != null) {
2364                    continue;
2365                }
2366            } else {
2367                if (!tag.equals(r.sbn.getTag())) {
2368                    continue;
2369                }
2370            }
2371            if (r.sbn.getPackageName().equals(pkg)) {
2372                return i;
2373            }
2374        }
2375        return -1;
2376    }
2377
2378    private void updateNotificationPulse() {
2379        synchronized (mNotificationList) {
2380            updateLightsLocked();
2381        }
2382    }
2383
2384    private static boolean isUidSystem(int uid) {
2385        final int appid = UserHandle.getAppId(uid);
2386        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
2387    }
2388
2389    private static boolean isCallerSystem() {
2390        return isUidSystem(Binder.getCallingUid());
2391    }
2392
2393    private static void checkCallerIsSystem() {
2394        if (isCallerSystem()) {
2395            return;
2396        }
2397        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
2398    }
2399
2400    private static void checkCallerIsSystemOrSameApp(String pkg) {
2401        if (isCallerSystem()) {
2402            return;
2403        }
2404        final int uid = Binder.getCallingUid();
2405        try {
2406            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
2407                    pkg, 0, UserHandle.getCallingUserId());
2408            if (!UserHandle.isSameApp(ai.uid, uid)) {
2409                throw new SecurityException("Calling uid " + uid + " gave package"
2410                        + pkg + " which is owned by uid " + ai.uid);
2411            }
2412        } catch (RemoteException re) {
2413            throw new SecurityException("Unknown package " + pkg + "\n" + re);
2414        }
2415    }
2416
2417    public class NotificationListeners extends ManagedServices {
2418
2419        public NotificationListeners() {
2420            super(getContext(), mHandler, mNotificationList, mUserProfiles);
2421        }
2422
2423        @Override
2424        protected Config getConfig() {
2425            Config c = new Config();
2426            c.caption = "notification listener";
2427            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
2428            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
2429            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
2430            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
2431            c.clientLabel = R.string.notification_listener_binding_label;
2432            return c;
2433        }
2434
2435        @Override
2436        protected IInterface asInterface(IBinder binder) {
2437            return INotificationListener.Stub.asInterface(binder);
2438        }
2439
2440        @Override
2441        public void onServiceAdded(ManagedServiceInfo info) {
2442            final INotificationListener listener = (INotificationListener) info.service;
2443            final String[] keys = getActiveNotificationKeys(listener);
2444            try {
2445                listener.onListenerConnected(new NotificationOrderUpdate(keys));
2446            } catch (RemoteException e) {
2447                // we tried
2448            }
2449        }
2450
2451        /**
2452         * asynchronously notify all listeners about a new notification
2453         */
2454        public void notifyPostedLocked(StatusBarNotification sbn) {
2455            // make a copy in case changes are made to the underlying Notification object
2456            final StatusBarNotification sbnClone = sbn.clone();
2457            for (final ManagedServiceInfo info : mServices) {
2458                if (info.isEnabledForCurrentProfiles()) {
2459                    final INotificationListener listener = (INotificationListener) info.service;
2460                    final String[] keys = getActiveNotificationKeys(listener);
2461                    if (keys.length > 0) {
2462                        mHandler.post(new Runnable() {
2463                            @Override
2464                            public void run() {
2465                                notifyPostedIfUserMatch(info, sbnClone, keys);
2466                            }
2467                        });
2468                    }
2469                }
2470            }
2471        }
2472
2473        /**
2474         * asynchronously notify all listeners about a removed notification
2475         */
2476        public void notifyRemovedLocked(StatusBarNotification sbn) {
2477            // make a copy in case changes are made to the underlying Notification object
2478            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
2479            // notification
2480            final StatusBarNotification sbnLight = sbn.cloneLight();
2481            for (final ManagedServiceInfo info : mServices) {
2482                if (info.isEnabledForCurrentProfiles()) {
2483                    final INotificationListener listener = (INotificationListener) info.service;
2484                    final String[] keys = getActiveNotificationKeys(listener);
2485                    mHandler.post(new Runnable() {
2486                        @Override
2487                        public void run() {
2488                            notifyRemovedIfUserMatch(info, sbnLight, keys);
2489                        }
2490                    });
2491                }
2492            }
2493        }
2494
2495        /**
2496         * asynchronously notify all listeners about a reordering of notifications
2497         * @param sbns an array of {@link StatusBarNotification}s to consider.  This code
2498         *             must not rely on mutable members of these objects, such as the
2499         *             {@link Notification}.
2500         */
2501        public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
2502            for (final ManagedServiceInfo serviceInfo : mServices) {
2503                mHandler.post(new Runnable() {
2504                    @Override
2505                    public void run() {
2506                        notifyOrderUpdateIfUserMatch(serviceInfo, sbns);
2507                    }
2508                });
2509            }
2510        }
2511
2512        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
2513                final StatusBarNotification sbn, String[] keys) {
2514            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2515                return;
2516            }
2517            final INotificationListener listener = (INotificationListener)info.service;
2518            try {
2519                listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys));
2520            } catch (RemoteException ex) {
2521                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
2522            }
2523        }
2524
2525        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
2526                String[] keys) {
2527            if (!info.enabledAndUserMatches(sbn.getUserId())) {
2528                return;
2529            }
2530            final INotificationListener listener = (INotificationListener)info.service;
2531            try {
2532                listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys));
2533            } catch (RemoteException ex) {
2534                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
2535            }
2536        }
2537
2538        /**
2539         * @param sbns an array of {@link StatusBarNotification}s to consider.  This code
2540         *             must not rely on mutable members of these objects, such as the
2541         *             {@link Notification}.
2542         */
2543        public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info,
2544                ArrayList<StatusBarNotification> sbns) {
2545            ArrayList<String> keys = new ArrayList<String>(sbns.size());
2546            for (StatusBarNotification sbn: sbns) {
2547                if (info.enabledAndUserMatches(sbn.getUserId())) {
2548                    keys.add(sbn.getKey());
2549                }
2550            }
2551            final INotificationListener listener = (INotificationListener)info.service;
2552            try {
2553                listener.onNotificationOrderUpdate(
2554                        new NotificationOrderUpdate(keys.toArray(new String[keys.size()])));
2555            } catch (RemoteException ex) {
2556                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
2557            }
2558        }
2559    }
2560}
2561