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