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