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