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