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