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