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