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