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