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