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