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