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