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