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