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