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