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