NotificationManagerService.java revision 8bc45cb78fafcd6165004addf9d8c538ee91b936
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.app.NotificationManager.IMPORTANCE_MIN;
20import static android.app.NotificationManager.IMPORTANCE_NONE;
21import static android.content.pm.PackageManager.FEATURE_LEANBACK;
22import static android.content.pm.PackageManager.FEATURE_TELEVISION;
23import static android.service.notification.NotificationListenerService
24        .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
25import static android.service.notification.NotificationListenerService
26        .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
27import static android.service.notification.NotificationListenerService
28        .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
29import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
30import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
31import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
32import static android.service.notification.NotificationListenerService.REASON_CANCEL;
33import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
34import static android.service.notification.NotificationListenerService.REASON_CLICK;
35import static android.service.notification.NotificationListenerService.REASON_ERROR;
36import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
37import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
38import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
39import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
40import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
41import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
42import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
43import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
44import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
45import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
46import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
47import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
48import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
49import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
50import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
51import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
52import static android.service.notification.NotificationListenerService.TRIM_FULL;
53import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
54
55import static android.view.Display.DEFAULT_DISPLAY;
56import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
57import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
58
59import android.Manifest;
60import android.annotation.NonNull;
61import android.annotation.Nullable;
62import android.app.ActivityManager;
63import android.app.ActivityManagerInternal;
64import android.app.AlarmManager;
65import android.app.AppGlobals;
66import android.app.AppOpsManager;
67import android.app.AutomaticZenRule;
68import android.app.NotificationChannelGroup;
69import android.app.backup.BackupManager;
70import android.app.IActivityManager;
71import android.app.INotificationManager;
72import android.app.ITransientNotification;
73import android.app.Notification;
74import android.app.NotificationChannel;
75import android.app.NotificationManager.Policy;
76import android.app.NotificationManager;
77import android.app.PendingIntent;
78import android.app.StatusBarManager;
79import android.app.usage.UsageEvents;
80import android.app.usage.UsageStatsManagerInternal;
81import android.companion.ICompanionDeviceManager;
82import android.content.BroadcastReceiver;
83import android.content.ComponentName;
84import android.content.ContentResolver;
85import android.content.Context;
86import android.content.Intent;
87import android.content.IntentFilter;
88import android.content.pm.ApplicationInfo;
89import android.content.pm.IPackageManager;
90import android.content.pm.PackageInfo;
91import android.content.pm.PackageManager;
92import android.content.pm.PackageManager.NameNotFoundException;
93import android.content.pm.ParceledListSlice;
94import android.content.res.Resources;
95import android.database.ContentObserver;
96import android.media.AudioManager;
97import android.media.AudioManagerInternal;
98import android.media.IRingtonePlayer;
99import android.media.ToneGenerator;
100import android.net.Uri;
101import android.os.Binder;
102import android.os.Build;
103import android.os.Bundle;
104import android.os.Environment;
105import android.os.Handler;
106import android.os.HandlerThread;
107import android.os.IBinder;
108import android.os.IInterface;
109import android.os.Looper;
110import android.os.Message;
111import android.os.Process;
112import android.os.RemoteException;
113import android.os.ServiceManager;
114import android.os.SystemClock;
115import android.os.SystemProperties;
116import android.os.UserHandle;
117import android.os.Vibrator;
118import android.os.VibrationEffect;
119import android.provider.Settings;
120import android.service.notification.Adjustment;
121import android.service.notification.Condition;
122import android.service.notification.IConditionProvider;
123import android.service.notification.INotificationListener;
124import android.service.notification.IStatusBarNotificationHolder;
125import android.service.notification.NotificationAssistantService;
126import android.service.notification.NotificationListenerService;
127import android.service.notification.NotificationRankingUpdate;
128import android.service.notification.NotificationRecordProto;
129import android.service.notification.NotificationServiceDumpProto;
130import android.service.notification.NotificationServiceProto;
131import android.service.notification.SnoozeCriterion;
132import android.service.notification.StatusBarNotification;
133import android.service.notification.ZenModeConfig;
134import android.service.notification.ZenModeProto;
135import android.telephony.PhoneStateListener;
136import android.telephony.TelephonyManager;
137import android.text.TextUtils;
138import android.util.ArrayMap;
139import android.util.ArraySet;
140import android.util.AtomicFile;
141import android.util.Log;
142import android.util.Slog;
143import android.util.SparseArray;
144import android.util.Xml;
145import android.util.proto.ProtoOutputStream;
146import android.view.WindowManagerInternal;
147import android.view.accessibility.AccessibilityEvent;
148import android.view.accessibility.AccessibilityManager;
149import android.widget.Toast;
150
151import com.android.internal.R;
152import com.android.internal.annotations.VisibleForTesting;
153import com.android.internal.logging.MetricsLogger;
154import com.android.internal.logging.nano.MetricsProto;
155import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
156import com.android.internal.statusbar.NotificationVisibility;
157import com.android.internal.util.ArrayUtils;
158import com.android.internal.util.DumpUtils;
159import com.android.internal.util.FastXmlSerializer;
160import com.android.internal.util.Preconditions;
161import com.android.server.DeviceIdleController;
162import com.android.server.EventLogTags;
163import com.android.server.LocalServices;
164import com.android.server.SystemService;
165import com.android.server.lights.Light;
166import com.android.server.lights.LightsManager;
167import com.android.server.notification.ManagedServices.ManagedServiceInfo;
168import com.android.server.policy.PhoneWindowManager;
169import com.android.server.statusbar.StatusBarManagerInternal;
170import com.android.server.notification.ManagedServices.UserProfiles;
171
172import libcore.io.IoUtils;
173
174import org.json.JSONException;
175import org.json.JSONObject;
176import org.xmlpull.v1.XmlPullParser;
177import org.xmlpull.v1.XmlPullParserException;
178import org.xmlpull.v1.XmlSerializer;
179
180import java.io.ByteArrayInputStream;
181import java.io.ByteArrayOutputStream;
182import java.io.File;
183import java.io.FileDescriptor;
184import java.io.FileInputStream;
185import java.io.FileNotFoundException;
186import java.io.FileOutputStream;
187import java.io.IOException;
188import java.io.InputStream;
189import java.io.OutputStream;
190import java.io.PrintWriter;
191import java.nio.charset.StandardCharsets;
192import java.util.ArrayDeque;
193import java.util.ArrayList;
194import java.util.Arrays;
195import java.util.Iterator;
196import java.util.List;
197import java.util.Map;
198import java.util.Map.Entry;
199import java.util.Objects;
200import java.util.concurrent.TimeUnit;
201
202/** {@hide} */
203public class NotificationManagerService extends SystemService {
204    static final String TAG = "NotificationService";
205    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
206    public static final boolean ENABLE_CHILD_NOTIFICATIONS
207            = SystemProperties.getBoolean("debug.child_notifs", true);
208
209    static final int MAX_PACKAGE_NOTIFICATIONS = 50;
210    static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 10f;
211
212    // message codes
213    static final int MESSAGE_TIMEOUT = 2;
214    static final int MESSAGE_SAVE_POLICY_FILE = 3;
215    static final int MESSAGE_SEND_RANKING_UPDATE = 4;
216    static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
217    static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
218
219    // ranking thread messages
220    private static final int MESSAGE_RECONSIDER_RANKING = 1000;
221    private static final int MESSAGE_RANKING_SORT = 1001;
222
223    static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
224    static final int SHORT_DELAY = 2000; // 2 seconds
225
226    static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
227
228    static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
229
230    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
231
232    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
233
234    static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
235    static final boolean ENABLE_BLOCKED_TOASTS = true;
236
237    // When #matchesCallFilter is called from the ringer, wait at most
238    // 3s to resolve the contacts. This timeout is required since
239    // ContactsProvider might take a long time to start up.
240    //
241    // Return STARRED_CONTACT when the timeout is hit in order to avoid
242    // missed calls in ZEN mode "Important".
243    static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
244    static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
245            ValidateNotificationPeople.STARRED_CONTACT;
246
247    /** notification_enqueue status value for a newly enqueued notification. */
248    private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
249
250    /** notification_enqueue status value for an existing notification. */
251    private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
252
253    /** notification_enqueue status value for an ignored notification. */
254    private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
255    private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
256
257    private static final long DELAY_FOR_ASSISTANT_TIME = 100;
258
259    private static final String ACTION_NOTIFICATION_TIMEOUT =
260            NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
261    private static final int REQUEST_CODE_TIMEOUT = 1;
262    private static final String SCHEME_TIMEOUT = "timeout";
263    private static final String EXTRA_KEY = "key";
264
265    private IActivityManager mAm;
266    private IPackageManager mPackageManager;
267    private PackageManager mPackageManagerClient;
268    AudioManager mAudioManager;
269    AudioManagerInternal mAudioManagerInternal;
270    @Nullable StatusBarManagerInternal mStatusBar;
271    Vibrator mVibrator;
272    private WindowManagerInternal mWindowManagerInternal;
273    private AlarmManager mAlarmManager;
274    private ICompanionDeviceManager mCompanionManager;
275
276    final IBinder mForegroundToken = new Binder();
277    private Handler mHandler;
278    private final HandlerThread mRankingThread = new HandlerThread("ranker",
279            Process.THREAD_PRIORITY_BACKGROUND);
280
281    private Light mNotificationLight;
282    Light mAttentionLight;
283
284    private long[] mFallbackVibrationPattern;
285    private boolean mUseAttentionLight;
286    boolean mSystemReady;
287
288    private boolean mDisableNotificationEffects;
289    private int mCallState;
290    private String mSoundNotificationKey;
291    private String mVibrateNotificationKey;
292
293    private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
294            new SparseArray<ArraySet<ManagedServiceInfo>>();
295    private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
296    private int mListenerHints;  // right now, all hints are global
297    private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
298
299    // for enabling and disabling notification pulse behavior
300    private boolean mScreenOn = true;
301    private boolean mInCall = false;
302    private boolean mNotificationPulseEnabled;
303
304    // for generating notification tones in-call
305    private ToneGenerator mInCallToneGenerator;
306    private final Object mInCallToneGeneratorLock = new Object();
307
308    // used as a mutex for access to all active notifications & listeners
309    final Object mNotificationLock = new Object();
310    final ArrayList<NotificationRecord> mNotificationList =
311            new ArrayList<NotificationRecord>();
312    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
313            new ArrayMap<String, NotificationRecord>();
314    final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
315    final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
316    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
317    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
318    final PolicyAccess mPolicyAccess = new PolicyAccess();
319
320    // The last key in this list owns the hardware.
321    ArrayList<String> mLights = new ArrayList<>();
322
323    private AppOpsManager mAppOps;
324    private UsageStatsManagerInternal mAppUsageStats;
325
326    private Archive mArchive;
327
328    // Persistent storage for notification policy
329    private AtomicFile mPolicyFile;
330
331    private static final int DB_VERSION = 1;
332
333    private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
334    private static final String ATTR_VERSION = "version";
335
336    private RankingHelper mRankingHelper;
337
338    private final UserProfiles mUserProfiles = new UserProfiles();
339    private NotificationListeners mListeners;
340    private NotificationAssistants mNotificationAssistants;
341    private ConditionProviders mConditionProviders;
342    private NotificationUsageStats mUsageStats;
343
344    private static final int MY_UID = Process.myUid();
345    private static final int MY_PID = Process.myPid();
346    private static final IBinder WHITELIST_TOKEN = new Binder();
347    private RankingHandler mRankingHandler;
348    private long mLastOverRateLogTime;
349    private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
350
351    private SnoozeHelper mSnoozeHelper;
352    private GroupHelper mGroupHelper;
353    private boolean mIsTelevision;
354
355    private static class Archive {
356        final int mBufferSize;
357        final ArrayDeque<StatusBarNotification> mBuffer;
358
359        public Archive(int size) {
360            mBufferSize = size;
361            mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
362        }
363
364        public String toString() {
365            final StringBuilder sb = new StringBuilder();
366            final int N = mBuffer.size();
367            sb.append("Archive (");
368            sb.append(N);
369            sb.append(" notification");
370            sb.append((N==1)?")":"s)");
371            return sb.toString();
372        }
373
374        public void record(StatusBarNotification nr) {
375            if (mBuffer.size() == mBufferSize) {
376                mBuffer.removeFirst();
377            }
378
379            // We don't want to store the heavy bits of the notification in the archive,
380            // but other clients in the system process might be using the object, so we
381            // store a (lightened) copy.
382            mBuffer.addLast(nr.cloneLight());
383        }
384
385        public Iterator<StatusBarNotification> descendingIterator() {
386            return mBuffer.descendingIterator();
387        }
388
389        public StatusBarNotification[] getArray(int count) {
390            if (count == 0) count = mBufferSize;
391            final StatusBarNotification[] a
392                    = new StatusBarNotification[Math.min(count, mBuffer.size())];
393            Iterator<StatusBarNotification> iter = descendingIterator();
394            int i=0;
395            while (iter.hasNext() && i < count) {
396                a[i++] = iter.next();
397            }
398            return a;
399        }
400
401    }
402
403    private void readPolicyXml(InputStream stream, boolean forRestore)
404            throws XmlPullParserException, NumberFormatException, IOException {
405        final XmlPullParser parser = Xml.newPullParser();
406        parser.setInput(stream, StandardCharsets.UTF_8.name());
407
408        while (parser.next() != END_DOCUMENT) {
409            mZenModeHelper.readXml(parser, forRestore);
410            mRankingHelper.readXml(parser, forRestore);
411        }
412    }
413
414    private void loadPolicyFile() {
415        if (DBG) Slog.d(TAG, "loadPolicyFile");
416        synchronized (mPolicyFile) {
417
418            FileInputStream infile = null;
419            try {
420                infile = mPolicyFile.openRead();
421                readPolicyXml(infile, false /*forRestore*/);
422            } catch (FileNotFoundException e) {
423                // No data yet
424            } catch (IOException e) {
425                Log.wtf(TAG, "Unable to read notification policy", e);
426            } catch (NumberFormatException e) {
427                Log.wtf(TAG, "Unable to parse notification policy", e);
428            } catch (XmlPullParserException e) {
429                Log.wtf(TAG, "Unable to parse notification policy", e);
430            } finally {
431                IoUtils.closeQuietly(infile);
432            }
433        }
434    }
435
436    public void savePolicyFile() {
437        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
438        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
439    }
440
441    private void handleSavePolicyFile() {
442        if (DBG) Slog.d(TAG, "handleSavePolicyFile");
443        synchronized (mPolicyFile) {
444            final FileOutputStream stream;
445            try {
446                stream = mPolicyFile.startWrite();
447            } catch (IOException e) {
448                Slog.w(TAG, "Failed to save policy file", e);
449                return;
450            }
451
452            try {
453                writePolicyXml(stream, false /*forBackup*/);
454                mPolicyFile.finishWrite(stream);
455            } catch (IOException e) {
456                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
457                mPolicyFile.failWrite(stream);
458            }
459        }
460        BackupManager.dataChanged(getContext().getPackageName());
461    }
462
463    private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
464        final XmlSerializer out = new FastXmlSerializer();
465        out.setOutput(stream, StandardCharsets.UTF_8.name());
466        out.startDocument(null, true);
467        out.startTag(null, TAG_NOTIFICATION_POLICY);
468        out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
469        mZenModeHelper.writeXml(out, forBackup);
470        mRankingHelper.writeXml(out, forBackup);
471        out.endTag(null, TAG_NOTIFICATION_POLICY);
472        out.endDocument();
473    }
474
475    /** Use this to check if a package can post a notification or toast. */
476    private boolean checkNotificationOp(String pkg, int uid) {
477        return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
478                == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
479    }
480
481    private static final class ToastRecord
482    {
483        final int pid;
484        final String pkg;
485        final ITransientNotification callback;
486        int duration;
487        Binder token;
488
489        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
490                    Binder token) {
491            this.pid = pid;
492            this.pkg = pkg;
493            this.callback = callback;
494            this.duration = duration;
495            this.token = token;
496        }
497
498        void update(int duration) {
499            this.duration = duration;
500        }
501
502        void dump(PrintWriter pw, String prefix, DumpFilter filter) {
503            if (filter != null && !filter.matches(pkg)) return;
504            pw.println(prefix + this);
505        }
506
507        @Override
508        public final String toString()
509        {
510            return "ToastRecord{"
511                + Integer.toHexString(System.identityHashCode(this))
512                + " pkg=" + pkg
513                + " callback=" + callback
514                + " duration=" + duration;
515        }
516    }
517
518    private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
519
520        @Override
521        public void onSetDisabled(int status) {
522            synchronized (mNotificationLock) {
523                mDisableNotificationEffects =
524                        (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
525                if (disableNotificationEffects(null) != null) {
526                    // cancel whatever's going on
527                    long identity = Binder.clearCallingIdentity();
528                    try {
529                        final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
530                        if (player != null) {
531                            player.stopAsync();
532                        }
533                    } catch (RemoteException e) {
534                    } finally {
535                        Binder.restoreCallingIdentity(identity);
536                    }
537
538                    identity = Binder.clearCallingIdentity();
539                    try {
540                        mVibrator.cancel();
541                    } finally {
542                        Binder.restoreCallingIdentity(identity);
543                    }
544                }
545            }
546        }
547
548        @Override
549        public void onClearAll(int callingUid, int callingPid, int userId) {
550            synchronized (mNotificationLock) {
551                cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
552                        /*includeCurrentProfiles*/ true);
553            }
554        }
555
556        @Override
557        public void onNotificationClick(int callingUid, int callingPid, String key) {
558            synchronized (mNotificationLock) {
559                NotificationRecord r = mNotificationsByKey.get(key);
560                if (r == null) {
561                    Log.w(TAG, "No notification with key: " + key);
562                    return;
563                }
564                final long now = System.currentTimeMillis();
565                MetricsLogger.action(r.getLogMaker(now)
566                        .setCategory(MetricsEvent.NOTIFICATION_ITEM)
567                        .setType(MetricsEvent.TYPE_ACTION));
568                EventLogTags.writeNotificationClicked(key,
569                        r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
570
571                StatusBarNotification sbn = r.sbn;
572                cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
573                        sbn.getId(), Notification.FLAG_AUTO_CANCEL,
574                        Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
575                        REASON_CLICK, null);
576            }
577        }
578
579        @Override
580        public void onNotificationActionClick(int callingUid, int callingPid, String key,
581                int actionIndex) {
582            synchronized (mNotificationLock) {
583                NotificationRecord r = mNotificationsByKey.get(key);
584                if (r == null) {
585                    Log.w(TAG, "No notification with key: " + key);
586                    return;
587                }
588                final long now = System.currentTimeMillis();
589                MetricsLogger.action(r.getLogMaker(now)
590                        .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
591                        .setType(MetricsEvent.TYPE_ACTION)
592                        .setSubtype(actionIndex));
593                EventLogTags.writeNotificationActionClicked(key, actionIndex,
594                        r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
595                // TODO: Log action click via UsageStats.
596            }
597        }
598
599        @Override
600        public void onNotificationClear(int callingUid, int callingPid,
601                String pkg, String tag, int id, int userId) {
602            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
603                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
604                    true, userId, REASON_CANCEL, null);
605        }
606
607        @Override
608        public void onPanelRevealed(boolean clearEffects, int items) {
609            MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
610            MetricsLogger.histogram(getContext(), "notification_load", items);
611            EventLogTags.writeNotificationPanelRevealed(items);
612            if (clearEffects) {
613                clearEffects();
614            }
615        }
616
617        @Override
618        public void onPanelHidden() {
619            MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
620            EventLogTags.writeNotificationPanelHidden();
621        }
622
623        @Override
624        public void clearEffects() {
625            synchronized (mNotificationLock) {
626                if (DBG) Slog.d(TAG, "clearEffects");
627                clearSoundLocked();
628                clearVibrateLocked();
629                clearLightsLocked();
630            }
631        }
632
633        @Override
634        public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
635                int uid, int initialPid, String message, int userId) {
636            Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
637                    + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
638            cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
639                    REASON_ERROR, null);
640            long ident = Binder.clearCallingIdentity();
641            try {
642                ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
643                        "Bad notification posted from package " + pkg
644                        + ": " + message);
645            } catch (RemoteException e) {
646            }
647            Binder.restoreCallingIdentity(ident);
648        }
649
650        @Override
651        public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
652                NotificationVisibility[] noLongerVisibleKeys) {
653            synchronized (mNotificationLock) {
654                for (NotificationVisibility nv : newlyVisibleKeys) {
655                    NotificationRecord r = mNotificationsByKey.get(nv.key);
656                    if (r == null) continue;
657                    r.setVisibility(true, nv.rank);
658                    nv.recycle();
659                }
660                // Note that we might receive this event after notifications
661                // have already left the system, e.g. after dismissing from the
662                // shade. Hence not finding notifications in
663                // mNotificationsByKey is not an exceptional condition.
664                for (NotificationVisibility nv : noLongerVisibleKeys) {
665                    NotificationRecord r = mNotificationsByKey.get(nv.key);
666                    if (r == null) continue;
667                    r.setVisibility(false, nv.rank);
668                    nv.recycle();
669                }
670            }
671        }
672
673        @Override
674        public void onNotificationExpansionChanged(String key,
675                boolean userAction, boolean expanded) {
676            synchronized (mNotificationLock) {
677                NotificationRecord r = mNotificationsByKey.get(key);
678                if (r != null) {
679                    r.stats.onExpansionChanged(userAction, expanded);
680                    final long now = System.currentTimeMillis();
681                    MetricsLogger.action(r.getLogMaker(now)
682                            .setCategory(MetricsEvent.NOTIFICATION_ITEM)
683                            .setType(MetricsEvent.TYPE_DETAIL));
684                    EventLogTags.writeNotificationExpansion(key,
685                            userAction ? 1 : 0, expanded ? 1 : 0,
686                            r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
687                }
688            }
689        }
690    };
691
692    private void clearSoundLocked() {
693        mSoundNotificationKey = null;
694        long identity = Binder.clearCallingIdentity();
695        try {
696            final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
697            if (player != null) {
698                player.stopAsync();
699            }
700        } catch (RemoteException e) {
701        } finally {
702            Binder.restoreCallingIdentity(identity);
703        }
704    }
705
706    private void clearVibrateLocked() {
707        mVibrateNotificationKey = null;
708        long identity = Binder.clearCallingIdentity();
709        try {
710            mVibrator.cancel();
711        } finally {
712            Binder.restoreCallingIdentity(identity);
713        }
714    }
715
716    private void clearLightsLocked() {
717        // light
718        mLights.clear();
719        updateLightsLocked();
720    }
721
722    private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
723        @Override
724        public void onReceive(Context context, Intent intent) {
725            String action = intent.getAction();
726            if (action == null) {
727                return;
728            }
729            if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
730                final NotificationRecord record;
731                synchronized (mNotificationLock) {
732                    record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
733                }
734                if (record != null) {
735                    cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
736                            record.sbn.getPackageName(), record.sbn.getTag(),
737                            record.sbn.getId(), 0,
738                            Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
739                            REASON_TIMEOUT, null);
740                }
741            }
742        }
743    };
744
745    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
746        @Override
747        public void onReceive(Context context, Intent intent) {
748            String action = intent.getAction();
749            if (action == null) {
750                return;
751            }
752
753            boolean queryRestart = false;
754            boolean queryRemove = false;
755            boolean packageChanged = false;
756            boolean cancelNotifications = true;
757            int reason = REASON_PACKAGE_CHANGED;
758
759            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
760                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
761                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
762                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
763                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
764                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
765                    || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
766                int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
767                        UserHandle.USER_ALL);
768                String pkgList[] = null;
769                int uidList[] = null;
770                boolean removingPackage = queryRemove &&
771                        !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
772                if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
773                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
774                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
775                    uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
776                } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
777                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
778                    reason = REASON_PACKAGE_SUSPENDED;
779                } else if (queryRestart) {
780                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
781                    uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
782                } else {
783                    Uri uri = intent.getData();
784                    if (uri == null) {
785                        return;
786                    }
787                    String pkgName = uri.getSchemeSpecificPart();
788                    if (pkgName == null) {
789                        return;
790                    }
791                    if (packageChanged) {
792                        // We cancel notifications for packages which have just been disabled
793                        try {
794                            final int enabled = mPackageManager.getApplicationEnabledSetting(
795                                    pkgName,
796                                    changeUserId != UserHandle.USER_ALL ? changeUserId :
797                                            UserHandle.USER_SYSTEM);
798                            if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
799                                    || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
800                                cancelNotifications = false;
801                            }
802                        } catch (IllegalArgumentException e) {
803                            // Package doesn't exist; probably racing with uninstall.
804                            // cancelNotifications is already true, so nothing to do here.
805                            if (DBG) {
806                                Slog.i(TAG, "Exception trying to look up app enabled setting", e);
807                            }
808                        } catch (RemoteException e) {
809                            // Failed to talk to PackageManagerService Should never happen!
810                        }
811                    }
812                    pkgList = new String[]{pkgName};
813                    uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
814                }
815                if (pkgList != null && (pkgList.length > 0)) {
816                    for (String pkgName : pkgList) {
817                        if (cancelNotifications) {
818                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
819                                    !queryRestart, changeUserId, reason, null);
820                        }
821                    }
822                }
823                mListeners.onPackagesChanged(removingPackage, pkgList);
824                mNotificationAssistants.onPackagesChanged(removingPackage, pkgList);
825                mConditionProviders.onPackagesChanged(removingPackage, pkgList);
826                mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
827                savePolicyFile();
828            }
829        }
830    };
831
832    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
833        @Override
834        public void onReceive(Context context, Intent intent) {
835            String action = intent.getAction();
836
837            if (action.equals(Intent.ACTION_SCREEN_ON)) {
838                // Keep track of screen on/off state, but do not turn off the notification light
839                // until user passes through the lock screen or views the notification.
840                mScreenOn = true;
841                updateNotificationPulse();
842            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
843                mScreenOn = false;
844                updateNotificationPulse();
845            } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
846                mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
847                        .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
848                updateNotificationPulse();
849                synchronized (mInCallToneGeneratorLock) {
850                    if (mInCall) {
851                        if (mInCallToneGenerator == null) {
852                            int relativeToneVolume = getContext().getResources().getInteger(
853                                    R.integer.config_inCallNotificationVolumeRelative);
854                            if (relativeToneVolume < ToneGenerator.MIN_VOLUME
855                                    || relativeToneVolume > ToneGenerator.MAX_VOLUME) {
856                                relativeToneVolume = ToneGenerator.MAX_VOLUME;
857                            }
858                            try {
859                                mInCallToneGenerator = new ToneGenerator(
860                                        AudioManager.STREAM_VOICE_CALL, relativeToneVolume);
861                            } catch (RuntimeException e) {
862                                Log.e(TAG, "Error creating local tone generator: " + e);
863                                mInCallToneGenerator = null;
864                            }
865                        }
866                    } else {
867                        if (mInCallToneGenerator != null) {
868                            mInCallToneGenerator.release();
869                            mInCallToneGenerator = null;
870                        }
871                     }
872                }
873            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
874                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
875                if (userHandle >= 0) {
876                    cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
877                            REASON_USER_STOPPED, null);
878                }
879            } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
880                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
881                if (userHandle >= 0) {
882                    cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
883                            REASON_PROFILE_TURNED_OFF, null);
884                }
885            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
886                // turn off LED when user passes through lock screen
887                mNotificationLight.turnOff();
888            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
889                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
890                // reload per-user settings
891                mSettingsObserver.update(null);
892                mUserProfiles.updateCache(context);
893                // Refresh managed services
894                mConditionProviders.onUserSwitched(user);
895                mListeners.onUserSwitched(user);
896                mNotificationAssistants.onUserSwitched(user);
897                mZenModeHelper.onUserSwitched(user);
898            } else if (action.equals(Intent.ACTION_USER_ADDED)) {
899                mUserProfiles.updateCache(context);
900            } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
901                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
902                mZenModeHelper.onUserRemoved(user);
903                mRankingHelper.onUserRemoved(user);
904                savePolicyFile();
905            } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
906                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
907                mConditionProviders.onUserUnlocked(user);
908                mListeners.onUserUnlocked(user);
909                mNotificationAssistants.onUserUnlocked(user);
910                mZenModeHelper.onUserUnlocked(user);
911            }
912        }
913    };
914
915    private final class SettingsObserver extends ContentObserver {
916        private final Uri NOTIFICATION_BADGING_URI
917                = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
918        private final Uri NOTIFICATION_LIGHT_PULSE_URI
919                = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
920        private final Uri NOTIFICATION_RATE_LIMIT_URI
921                = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
922
923        SettingsObserver(Handler handler) {
924            super(handler);
925        }
926
927        void observe() {
928            ContentResolver resolver = getContext().getContentResolver();
929            resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
930                    false, this, UserHandle.USER_ALL);
931            resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
932                    false, this, UserHandle.USER_ALL);
933            resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
934                    false, this, UserHandle.USER_ALL);
935            update(null);
936        }
937
938        @Override public void onChange(boolean selfChange, Uri uri) {
939            update(uri);
940        }
941
942        public void update(Uri uri) {
943            ContentResolver resolver = getContext().getContentResolver();
944            if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
945                boolean pulseEnabled = Settings.System.getInt(resolver,
946                            Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
947                if (mNotificationPulseEnabled != pulseEnabled) {
948                    mNotificationPulseEnabled = pulseEnabled;
949                    updateNotificationPulse();
950                }
951            }
952            if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
953                mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
954                            Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
955            }
956            if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
957                mRankingHelper.updateBadgingEnabled();
958            }
959        }
960    }
961
962    private SettingsObserver mSettingsObserver;
963    private ZenModeHelper mZenModeHelper;
964
965    static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
966        int[] ar = r.getIntArray(resid);
967        if (ar == null) {
968            return def;
969        }
970        final int len = ar.length > maxlen ? maxlen : ar.length;
971        long[] out = new long[len];
972        for (int i=0; i<len; i++) {
973            out[i] = ar[i];
974        }
975        return out;
976    }
977
978    public NotificationManagerService(Context context) {
979        super(context);
980        Notification.processWhitelistToken = WHITELIST_TOKEN;
981    }
982
983    // TODO - replace these methods with a single VisibleForTesting constructor
984    @VisibleForTesting
985    void setAudioManager(AudioManager audioMananger) {
986        mAudioManager = audioMananger;
987    }
988
989    @VisibleForTesting
990    void setVibrator(Vibrator vibrator) {
991        mVibrator = vibrator;
992    }
993
994    @VisibleForTesting
995    void setLights(Light light) {
996        mNotificationLight = light;
997        mAttentionLight = light;
998        mNotificationPulseEnabled = true;
999    }
1000
1001    @VisibleForTesting
1002    void setScreenOn(boolean on) {
1003        mScreenOn = on;
1004    }
1005
1006    @VisibleForTesting
1007    void addNotification(NotificationRecord r) {
1008        mNotificationList.add(r);
1009        mNotificationsByKey.put(r.sbn.getKey(), r);
1010        if (r.sbn.isGroup()) {
1011            mSummaryByGroupKey.put(r.getGroupKey(), r);
1012        }
1013    }
1014
1015    @VisibleForTesting
1016    void addEnqueuedNotification(NotificationRecord r) {
1017        mEnqueuedNotifications.add(r);
1018    }
1019
1020    @VisibleForTesting
1021    void setSystemReady(boolean systemReady) {
1022        mSystemReady = systemReady;
1023    }
1024
1025    @VisibleForTesting
1026    void setHandler(Handler handler) {
1027        mHandler = handler;
1028    }
1029
1030    @VisibleForTesting
1031    void setFallbackVibrationPattern(long[] vibrationPattern) {
1032        mFallbackVibrationPattern = vibrationPattern;
1033    }
1034
1035    @VisibleForTesting
1036    void setPackageManager(IPackageManager packageManager) {
1037        mPackageManager = packageManager;
1038    }
1039
1040    @VisibleForTesting
1041    void setRankingHelper(RankingHelper rankingHelper) {
1042        mRankingHelper = rankingHelper;
1043    }
1044
1045    @VisibleForTesting
1046    void setIsTelevision(boolean isTelevision) {
1047        mIsTelevision = isTelevision;
1048    }
1049
1050    // TODO: Tests should call onStart instead once the methods above are removed.
1051    @VisibleForTesting
1052    void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
1053            LightsManager lightsManager, NotificationListeners notificationListeners,
1054            ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper) {
1055        Resources resources = getContext().getResources();
1056        mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1057                Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1058                DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1059
1060        mAm = ActivityManager.getService();
1061        mPackageManager = packageManager;
1062        mPackageManagerClient = packageManagerClient;
1063        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1064        mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1065        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1066        mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1067        mCompanionManager = companionManager;
1068
1069        mHandler = new WorkerHandler(looper);
1070        mRankingThread.start();
1071        String[] extractorNames;
1072        try {
1073            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1074        } catch (Resources.NotFoundException e) {
1075            extractorNames = new String[0];
1076        }
1077        mUsageStats = new NotificationUsageStats(getContext());
1078        mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1079        mRankingHelper = new RankingHelper(getContext(),
1080                getContext().getPackageManager(),
1081                mRankingHandler,
1082                mUsageStats,
1083                extractorNames);
1084        mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
1085        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1086        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1087            @Override
1088            public void onConfigChanged() {
1089                savePolicyFile();
1090            }
1091
1092            @Override
1093            void onZenModeChanged() {
1094                sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1095                getContext().sendBroadcastAsUser(
1096                        new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1097                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1098                        UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1099                synchronized (mNotificationLock) {
1100                    updateInterruptionFilterLocked();
1101                }
1102            }
1103
1104            @Override
1105            void onPolicyChanged() {
1106                sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1107            }
1108        });
1109        mSnoozeHelper = snoozeHelper;
1110        mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
1111            @Override
1112            public void addAutoGroup(String key) {
1113                synchronized (mNotificationLock) {
1114                    addAutogroupKeyLocked(key);
1115                }
1116                mRankingHandler.requestSort(false);
1117            }
1118
1119            @Override
1120            public void removeAutoGroup(String key) {
1121                synchronized (mNotificationLock) {
1122                    removeAutogroupKeyLocked(key);
1123                }
1124                mRankingHandler.requestSort(false);
1125            }
1126
1127            @Override
1128            public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1129                createAutoGroupSummary(userId, pkg, triggeringKey);
1130            }
1131
1132            @Override
1133            public void removeAutoGroupSummary(int userId, String pkg) {
1134                synchronized (mNotificationLock) {
1135                    clearAutogroupSummaryLocked(userId, pkg);
1136                }
1137            }
1138        });
1139
1140        final File systemDir = new File(Environment.getDataDirectory(), "system");
1141        mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
1142
1143        loadPolicyFile();
1144
1145        // This is a ManagedServices object that keeps track of the listeners.
1146        mListeners = notificationListeners;
1147
1148        // This is a MangedServices object that keeps track of the assistant.
1149        mNotificationAssistants = new NotificationAssistants();
1150
1151        mStatusBar = getLocalService(StatusBarManagerInternal.class);
1152        if (mStatusBar != null) {
1153            mStatusBar.setNotificationDelegate(mNotificationDelegate);
1154        }
1155
1156        mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1157        mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1158
1159        mFallbackVibrationPattern = getLongArray(resources,
1160                R.array.config_notificationFallbackVibePattern,
1161                VIBRATE_PATTERN_MAXLEN,
1162                DEFAULT_VIBRATE_PATTERN);
1163
1164        mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1165
1166        // Don't start allowing notifications until the setup wizard has run once.
1167        // After that, including subsequent boots, init with notifications turned on.
1168        // This works on the first boot because the setup wizard will toggle this
1169        // flag at least once and we'll go back to 0 after that.
1170        if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1171                    Settings.Global.DEVICE_PROVISIONED, 0)) {
1172            mDisableNotificationEffects = true;
1173        }
1174        mZenModeHelper.initZenMode();
1175        mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1176
1177        mUserProfiles.updateCache(getContext());
1178        listenForCallState();
1179
1180        // register for various Intents
1181        IntentFilter filter = new IntentFilter();
1182        filter.addAction(Intent.ACTION_SCREEN_ON);
1183        filter.addAction(Intent.ACTION_SCREEN_OFF);
1184        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1185        filter.addAction(Intent.ACTION_USER_PRESENT);
1186        filter.addAction(Intent.ACTION_USER_STOPPED);
1187        filter.addAction(Intent.ACTION_USER_SWITCHED);
1188        filter.addAction(Intent.ACTION_USER_ADDED);
1189        filter.addAction(Intent.ACTION_USER_REMOVED);
1190        filter.addAction(Intent.ACTION_USER_UNLOCKED);
1191        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1192        getContext().registerReceiver(mIntentReceiver, filter);
1193
1194        IntentFilter pkgFilter = new IntentFilter();
1195        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1196        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1197        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1198        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1199        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1200        pkgFilter.addDataScheme("package");
1201        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1202                null);
1203
1204        IntentFilter suspendedPkgFilter = new IntentFilter();
1205        suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1206        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1207                suspendedPkgFilter, null, null);
1208
1209        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1210        getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1211                null);
1212
1213        IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1214        timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1215        getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1216
1217        mSettingsObserver = new SettingsObserver(mHandler);
1218
1219        mArchive = new Archive(resources.getInteger(
1220                R.integer.config_notificationServiceArchiveSize));
1221
1222        mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1223                || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1224    }
1225
1226    @Override
1227    public void onStart() {
1228        SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1229            @Override
1230            public void repost(int userId, NotificationRecord r) {
1231                try {
1232                    if (DBG) {
1233                        Slog.d(TAG, "Reposting " + r.getKey());
1234                    }
1235                    enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1236                            r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1237                            r.sbn.getNotification(), userId);
1238                } catch (Exception e) {
1239                    Slog.e(TAG, "Cannot un-snooze notification", e);
1240                }
1241            }
1242        }, mUserProfiles);
1243
1244        init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
1245                getLocalService(LightsManager.class), new NotificationListeners(),
1246                null, snoozeHelper);
1247        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1248        publishLocalService(NotificationManagerInternal.class, mInternalService);
1249    }
1250
1251    private void sendRegisteredOnlyBroadcast(String action) {
1252        getContext().sendBroadcastAsUser(new Intent(action)
1253                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1254    }
1255
1256    @Override
1257    public void onBootPhase(int phase) {
1258        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1259            // no beeping until we're basically done booting
1260            mSystemReady = true;
1261
1262            // Grab our optional AudioService
1263            mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1264            mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1265            mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1266            mZenModeHelper.onSystemReady();
1267        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1268            // This observer will force an update when observe is called, causing us to
1269            // bind to listener services.
1270            mSettingsObserver.observe();
1271            mListeners.onBootPhaseAppsCanStart();
1272            mNotificationAssistants.onBootPhaseAppsCanStart();
1273            mConditionProviders.onBootPhaseAppsCanStart();
1274        }
1275    }
1276
1277    private void updateListenerHintsLocked() {
1278        final int hints = calculateHints();
1279        if (hints == mListenerHints) return;
1280        ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1281        mListenerHints = hints;
1282        scheduleListenerHintsChanged(hints);
1283    }
1284
1285    private void updateEffectsSuppressorLocked() {
1286        final long updatedSuppressedEffects = calculateSuppressedEffects();
1287        if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1288        final List<ComponentName> suppressors = getSuppressors();
1289        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1290        mEffectsSuppressors = suppressors;
1291        mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1292        sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1293    }
1294
1295    private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1296            boolean fromListener) {
1297        if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1298            // cancel
1299            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1300                    UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1301                    null);
1302        }
1303        mRankingHelper.updateNotificationChannel(pkg, uid, channel);
1304
1305        final NotificationChannel modifiedChannel =
1306                mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1307
1308        if (!fromListener) {
1309            mListeners.notifyNotificationChannelChanged(
1310                    pkg, UserHandle.getUserHandleForUid(uid),
1311                    modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1312        }
1313
1314        synchronized (mNotificationLock) {
1315            final int N = mNotificationList.size();
1316            for (int i = N - 1; i >= 0; --i) {
1317                NotificationRecord r = mNotificationList.get(i);
1318                if (r.sbn.getPackageName().equals(pkg)
1319                        && r.sbn.getUid() == uid
1320                        && channel.getId() != null
1321                        && channel.getId().equals(r.getChannel().getId())) {
1322                    r.updateNotificationChannel(modifiedChannel);
1323                }
1324            }
1325        }
1326        mRankingHandler.requestSort(true);
1327        savePolicyFile();
1328    }
1329
1330    private ArrayList<ComponentName> getSuppressors() {
1331        ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1332        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1333            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1334
1335            for (ManagedServiceInfo info : serviceInfoList) {
1336                names.add(info.component);
1337            }
1338        }
1339
1340        return names;
1341    }
1342
1343    private boolean removeDisabledHints(ManagedServiceInfo info) {
1344        return removeDisabledHints(info, 0);
1345    }
1346
1347    private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1348        boolean removed = false;
1349
1350        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1351            final int hint = mListenersDisablingEffects.keyAt(i);
1352            final ArraySet<ManagedServiceInfo> listeners =
1353                    mListenersDisablingEffects.valueAt(i);
1354
1355            if (hints == 0 || (hint & hints) == hint) {
1356                removed = removed || listeners.remove(info);
1357            }
1358        }
1359
1360        return removed;
1361    }
1362
1363    private void addDisabledHints(ManagedServiceInfo info, int hints) {
1364        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1365            addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1366        }
1367
1368        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1369            addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1370        }
1371
1372        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1373            addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1374        }
1375    }
1376
1377    private void addDisabledHint(ManagedServiceInfo info, int hint) {
1378        if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1379            mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1380        }
1381
1382        ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1383        hintListeners.add(info);
1384    }
1385
1386    private int calculateHints() {
1387        int hints = 0;
1388        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1389            int hint = mListenersDisablingEffects.keyAt(i);
1390            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1391
1392            if (!serviceInfoList.isEmpty()) {
1393                hints |= hint;
1394            }
1395        }
1396
1397        return hints;
1398    }
1399
1400    private long calculateSuppressedEffects() {
1401        int hints = calculateHints();
1402        long suppressedEffects = 0;
1403
1404        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1405            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1406        }
1407
1408        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1409            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1410        }
1411
1412        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1413            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1414        }
1415
1416        return suppressedEffects;
1417    }
1418
1419    private void updateInterruptionFilterLocked() {
1420        int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1421        if (interruptionFilter == mInterruptionFilter) return;
1422        mInterruptionFilter = interruptionFilter;
1423        scheduleInterruptionFilterChanged(interruptionFilter);
1424    }
1425
1426    @VisibleForTesting
1427    INotificationManager getBinderService() {
1428        return INotificationManager.Stub.asInterface(mService);
1429    }
1430
1431    @VisibleForTesting
1432    NotificationManagerInternal getInternalService() {
1433        return mInternalService;
1434    }
1435
1436    private final IBinder mService = new INotificationManager.Stub() {
1437        // Toasts
1438        // ============================================================================
1439
1440        @Override
1441        public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1442        {
1443            if (DBG) {
1444                Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1445                        + " duration=" + duration);
1446            }
1447
1448            if (pkg == null || callback == null) {
1449                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1450                return ;
1451            }
1452
1453            final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1454            final boolean isPackageSuspended =
1455                    isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1456
1457            if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1458                    (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1459                            || isPackageSuspended)) {
1460                Slog.e(TAG, "Suppressing toast from package " + pkg
1461                        + (isPackageSuspended
1462                                ? " due to package suspended by administrator."
1463                                : " by user request."));
1464                return;
1465            }
1466
1467            synchronized (mToastQueue) {
1468                int callingPid = Binder.getCallingPid();
1469                long callingId = Binder.clearCallingIdentity();
1470                try {
1471                    ToastRecord record;
1472                    int index = indexOfToastLocked(pkg, callback);
1473                    // If it's already in the queue, we update it in place, we don't
1474                    // move it to the end of the queue.
1475                    if (index >= 0) {
1476                        record = mToastQueue.get(index);
1477                        record.update(duration);
1478                    } else {
1479                        // Limit the number of toasts that any given package except the android
1480                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
1481                        if (!isSystemToast) {
1482                            int count = 0;
1483                            final int N = mToastQueue.size();
1484                            for (int i=0; i<N; i++) {
1485                                 final ToastRecord r = mToastQueue.get(i);
1486                                 if (r.pkg.equals(pkg)) {
1487                                     count++;
1488                                     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1489                                         Slog.e(TAG, "Package has already posted " + count
1490                                                + " toasts. Not showing more. Package=" + pkg);
1491                                         return;
1492                                     }
1493                                 }
1494                            }
1495                        }
1496
1497                        Binder token = new Binder();
1498                        mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1499                        record = new ToastRecord(callingPid, pkg, callback, duration, token);
1500                        mToastQueue.add(record);
1501                        index = mToastQueue.size() - 1;
1502                        keepProcessAliveIfNeededLocked(callingPid);
1503                    }
1504                    // If it's at index 0, it's the current toast.  It doesn't matter if it's
1505                    // new or just been updated.  Call back and tell it to show itself.
1506                    // If the callback fails, this will remove it from the list, so don't
1507                    // assume that it's valid after this.
1508                    if (index == 0) {
1509                        showNextToastLocked();
1510                    }
1511                } finally {
1512                    Binder.restoreCallingIdentity(callingId);
1513                }
1514            }
1515        }
1516
1517        @Override
1518        public void cancelToast(String pkg, ITransientNotification callback) {
1519            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1520
1521            if (pkg == null || callback == null) {
1522                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1523                return ;
1524            }
1525
1526            synchronized (mToastQueue) {
1527                long callingId = Binder.clearCallingIdentity();
1528                try {
1529                    int index = indexOfToastLocked(pkg, callback);
1530                    if (index >= 0) {
1531                        cancelToastLocked(index);
1532                    } else {
1533                        Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1534                                + " callback=" + callback);
1535                    }
1536                } finally {
1537                    Binder.restoreCallingIdentity(callingId);
1538                }
1539            }
1540        }
1541
1542        @Override
1543        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1544                Notification notification, int userId) throws RemoteException {
1545            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1546                    Binder.getCallingPid(), tag, id, notification, userId);
1547        }
1548
1549        @Override
1550        public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1551            checkCallerIsSystemOrSameApp(pkg);
1552            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1553                    Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1554            // Don't allow client applications to cancel foreground service notis or autobundled
1555            // summaries.
1556            final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1557                    (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1558            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1559                    mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1560        }
1561
1562        @Override
1563        public void cancelAllNotifications(String pkg, int userId) {
1564            checkCallerIsSystemOrSameApp(pkg);
1565
1566            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1567                    Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1568
1569            // Calling from user space, don't allow the canceling of actively
1570            // running foreground services.
1571            cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1572                    pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1573                    REASON_APP_CANCEL_ALL, null);
1574        }
1575
1576        @Override
1577        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1578            checkCallerIsSystem();
1579
1580            mRankingHelper.setEnabled(pkg, uid, enabled);
1581            // Now, cancel any outstanding notifications that are part of a just-disabled app
1582            if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1583                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1584                        UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1585            }
1586            savePolicyFile();
1587        }
1588
1589        /**
1590         * Use this when you just want to know if notifications are OK for this package.
1591         */
1592        @Override
1593        public boolean areNotificationsEnabled(String pkg) {
1594            return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1595        }
1596
1597        /**
1598         * Use this when you just want to know if notifications are OK for this package.
1599         */
1600        @Override
1601        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1602            checkCallerIsSystemOrSameApp(pkg);
1603
1604            return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1605        }
1606
1607        @Override
1608        public int getPackageImportance(String pkg) {
1609            checkCallerIsSystemOrSameApp(pkg);
1610            return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1611        }
1612
1613        @Override
1614        public boolean canShowBadge(String pkg, int uid) {
1615            checkCallerIsSystem();
1616            return mRankingHelper.canShowBadge(pkg, uid);
1617        }
1618
1619        @Override
1620        public void setShowBadge(String pkg, int uid, boolean showBadge) {
1621            checkCallerIsSystem();
1622            mRankingHelper.setShowBadge(pkg, uid, showBadge);
1623            savePolicyFile();
1624        }
1625
1626        @Override
1627        public void createNotificationChannelGroups(String pkg,
1628                ParceledListSlice channelGroupList) throws RemoteException {
1629            checkCallerIsSystemOrSameApp(pkg);
1630            List<NotificationChannelGroup> groups = channelGroupList.getList();
1631            final int groupSize = groups.size();
1632            for (int i = 0; i < groupSize; i++) {
1633                final NotificationChannelGroup group = groups.get(i);
1634                Preconditions.checkNotNull(group, "group in list is null");
1635                mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1636                        true /* fromTargetApp */);
1637                mListeners.notifyNotificationChannelGroupChanged(pkg,
1638                        UserHandle.of(UserHandle.getCallingUserId()), group,
1639                        NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1640            }
1641            savePolicyFile();
1642        }
1643
1644        private void createNotificationChannelsImpl(String pkg, int uid,
1645                ParceledListSlice channelsList) {
1646            List<NotificationChannel> channels = channelsList.getList();
1647            final int channelsSize = channels.size();
1648            for (int i = 0; i < channelsSize; i++) {
1649                final NotificationChannel channel = channels.get(i);
1650                Preconditions.checkNotNull(channel, "channel in list is null");
1651                mRankingHelper.createNotificationChannel(pkg, uid, channel,
1652                        true /* fromTargetApp */);
1653                mListeners.notifyNotificationChannelChanged(pkg,
1654                        UserHandle.getUserHandleForUid(uid),
1655                        mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1656                        NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1657            }
1658            savePolicyFile();
1659        }
1660
1661        @Override
1662        public void createNotificationChannels(String pkg,
1663                ParceledListSlice channelsList) throws RemoteException {
1664            checkCallerIsSystemOrSameApp(pkg);
1665            createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1666        }
1667
1668        @Override
1669        public void createNotificationChannelsForPackage(String pkg, int uid,
1670                ParceledListSlice channelsList) throws RemoteException {
1671            checkCallerIsSystem();
1672            createNotificationChannelsImpl(pkg, uid, channelsList);
1673        }
1674
1675        @Override
1676        public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1677            checkCallerIsSystemOrSameApp(pkg);
1678            return mRankingHelper.getNotificationChannel(
1679                    pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1680        }
1681
1682        @Override
1683        public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1684                String channelId, boolean includeDeleted) {
1685            checkCallerIsSystem();
1686            return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1687        }
1688
1689        @Override
1690        public void deleteNotificationChannel(String pkg, String channelId) {
1691            checkCallerIsSystemOrSameApp(pkg);
1692            final int callingUid = Binder.getCallingUid();
1693            if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1694                throw new IllegalArgumentException("Cannot delete default channel");
1695            }
1696            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1697                    UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1698            mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1699            mListeners.notifyNotificationChannelChanged(pkg,
1700                    UserHandle.getUserHandleForUid(callingUid),
1701                    mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1702                    NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1703            savePolicyFile();
1704        }
1705
1706        @Override
1707        public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1708                String pkg) {
1709            checkCallerIsSystemOrSameApp(pkg);
1710            return new ParceledListSlice<>(new ArrayList(
1711                    mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1712        }
1713
1714        @Override
1715        public void deleteNotificationChannelGroup(String pkg, String groupId) {
1716            checkCallerIsSystemOrSameApp(pkg);
1717
1718            final int callingUid = Binder.getCallingUid();
1719            NotificationChannelGroup groupToDelete =
1720                    mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1721            if (groupToDelete != null) {
1722                List<NotificationChannel> deletedChannels =
1723                        mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1724                for (int i = 0; i < deletedChannels.size(); i++) {
1725                    final NotificationChannel deletedChannel = deletedChannels.get(i);
1726                    cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1727                            true,
1728                            UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1729                            null);
1730                    mListeners.notifyNotificationChannelChanged(pkg,
1731                            UserHandle.getUserHandleForUid(callingUid),
1732                            deletedChannel,
1733                            NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1734                }
1735                mListeners.notifyNotificationChannelGroupChanged(
1736                        pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1737                        NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1738                savePolicyFile();
1739            }
1740        }
1741
1742        @Override
1743        public void updateNotificationChannelForPackage(String pkg, int uid,
1744                NotificationChannel channel) {
1745            enforceSystemOrSystemUI("Caller not system or systemui");
1746            Preconditions.checkNotNull(channel);
1747            updateNotificationChannelInt(pkg, uid, channel, false);
1748        }
1749
1750        @Override
1751        public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1752                int uid, boolean includeDeleted) {
1753            enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1754            return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1755        }
1756
1757        @Override
1758        public int getNumNotificationChannelsForPackage(String pkg, int uid,
1759                boolean includeDeleted) {
1760            enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1761            return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1762                    .getList().size();
1763        }
1764
1765        @Override
1766        public boolean onlyHasDefaultChannel(String pkg, int uid) {
1767            enforceSystemOrSystemUI("onlyHasDefaultChannel");
1768            return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1769        }
1770
1771        @Override
1772        public int getDeletedChannelCount(String pkg, int uid) {
1773            enforceSystemOrSystemUI("getDeletedChannelCount");
1774            return mRankingHelper.getDeletedChannelCount(pkg, uid);
1775        }
1776
1777        @Override
1778        public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1779                String pkg, int uid, boolean includeDeleted) {
1780            checkCallerIsSystem();
1781            return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1782        }
1783
1784        @Override
1785        public NotificationChannelGroup getNotificationChannelGroupForPackage(
1786                String groupId, String pkg, int uid) {
1787            enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1788            return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1789        }
1790
1791        @Override
1792        public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1793            checkCallerIsSystemOrSameApp(pkg);
1794            return mRankingHelper.getNotificationChannels(
1795                    pkg, Binder.getCallingUid(), false /* includeDeleted */);
1796        }
1797
1798        @Override
1799        public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1800            checkCallerIsSystem();
1801
1802            // Cancel posted notifications
1803            cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1804                    UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1805
1806            // Listener & assistant
1807            mListeners.onPackagesChanged(true, new String[] {packageName});
1808            mNotificationAssistants.onPackagesChanged(true, new String[] {packageName});
1809
1810            // Zen
1811            mConditionProviders.onPackagesChanged(true, new String[] {packageName});
1812
1813            // Reset notification preferences
1814            if (!fromApp) {
1815                mRankingHelper.onPackagesChanged(true, UserHandle.getCallingUserId(),
1816                        new String[]{packageName}, new int[]{uid});
1817            }
1818
1819            savePolicyFile();
1820        }
1821
1822
1823        /**
1824         * System-only API for getting a list of current (i.e. not cleared) notifications.
1825         *
1826         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1827         * @returns A list of all the notifications, in natural order.
1828         */
1829        @Override
1830        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1831            // enforce() will ensure the calling uid has the correct permission
1832            getContext().enforceCallingOrSelfPermission(
1833                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1834                    "NotificationManagerService.getActiveNotifications");
1835
1836            StatusBarNotification[] tmp = null;
1837            int uid = Binder.getCallingUid();
1838
1839            // noteOp will check to make sure the callingPkg matches the uid
1840            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1841                    == AppOpsManager.MODE_ALLOWED) {
1842                synchronized (mNotificationLock) {
1843                    tmp = new StatusBarNotification[mNotificationList.size()];
1844                    final int N = mNotificationList.size();
1845                    for (int i=0; i<N; i++) {
1846                        tmp[i] = mNotificationList.get(i).sbn;
1847                    }
1848                }
1849            }
1850            return tmp;
1851        }
1852
1853        /**
1854         * Public API for getting a list of current notifications for the calling package/uid.
1855         *
1856         * Note that since notification posting is done asynchronously, this will not return
1857         * notifications that are in the process of being posted.
1858         *
1859         * @returns A list of all the package's notifications, in natural order.
1860         */
1861        @Override
1862        public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1863                int incomingUserId) {
1864            checkCallerIsSystemOrSameApp(pkg);
1865            int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1866                    Binder.getCallingUid(), incomingUserId, true, false,
1867                    "getAppActiveNotifications", pkg);
1868            synchronized (mNotificationLock) {
1869                final ArrayMap<String, StatusBarNotification> map
1870                        = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
1871                final int N = mNotificationList.size();
1872                for (int i = 0; i < N; i++) {
1873                    StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1874                            mNotificationList.get(i).sbn);
1875                    if (sbn != null) {
1876                        map.put(sbn.getKey(), sbn);
1877                    }
1878                }
1879                for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
1880                    StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
1881                    if (sbn != null) {
1882                        map.put(sbn.getKey(), sbn);
1883                    }
1884                }
1885                final int M = mEnqueuedNotifications.size();
1886                for (int i = 0; i < M; i++) {
1887                    StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1888                            mEnqueuedNotifications.get(i).sbn);
1889                    if (sbn != null) {
1890                        map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
1891                    }
1892                }
1893                final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
1894                list.addAll(map.values());
1895                return new ParceledListSlice<StatusBarNotification>(list);
1896            }
1897        }
1898
1899        private StatusBarNotification sanitizeSbn(String pkg, int userId,
1900                StatusBarNotification sbn) {
1901            if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1902                    && (sbn.getNotification().flags
1903                    & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1904                // We could pass back a cloneLight() but clients might get confused and
1905                // try to send this thing back to notify() again, which would not work
1906                // very well.
1907                return new StatusBarNotification(
1908                        sbn.getPackageName(),
1909                        sbn.getOpPkg(),
1910                        sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1911                        sbn.getNotification().clone(),
1912                        sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
1913            }
1914            return null;
1915        }
1916
1917        /**
1918         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1919         *
1920         * Requires ACCESS_NOTIFICATIONS which is signature|system.
1921         */
1922        @Override
1923        public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1924            // enforce() will ensure the calling uid has the correct permission
1925            getContext().enforceCallingOrSelfPermission(
1926                    android.Manifest.permission.ACCESS_NOTIFICATIONS,
1927                    "NotificationManagerService.getHistoricalNotifications");
1928
1929            StatusBarNotification[] tmp = null;
1930            int uid = Binder.getCallingUid();
1931
1932            // noteOp will check to make sure the callingPkg matches the uid
1933            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1934                    == AppOpsManager.MODE_ALLOWED) {
1935                synchronized (mArchive) {
1936                    tmp = mArchive.getArray(count);
1937                }
1938            }
1939            return tmp;
1940        }
1941
1942        /**
1943         * Register a listener binder directly with the notification manager.
1944         *
1945         * Only works with system callers. Apps should extend
1946         * {@link android.service.notification.NotificationListenerService}.
1947         */
1948        @Override
1949        public void registerListener(final INotificationListener listener,
1950                final ComponentName component, final int userid) {
1951            enforceSystemOrSystemUI("INotificationManager.registerListener");
1952            mListeners.registerService(listener, component, userid);
1953        }
1954
1955        /**
1956         * Remove a listener binder directly
1957         */
1958        @Override
1959        public void unregisterListener(INotificationListener token, int userid) {
1960            mListeners.unregisterService(token, userid);
1961        }
1962
1963        /**
1964         * Allow an INotificationListener to simulate a "clear all" operation.
1965         *
1966         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1967         *
1968         * @param token The binder for the listener, to check that the caller is allowed
1969         */
1970        @Override
1971        public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1972            final int callingUid = Binder.getCallingUid();
1973            final int callingPid = Binder.getCallingPid();
1974            long identity = Binder.clearCallingIdentity();
1975            try {
1976                synchronized (mNotificationLock) {
1977                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1978                    if (keys != null) {
1979                        final int N = keys.length;
1980                        for (int i = 0; i < N; i++) {
1981                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
1982                            if (r == null) continue;
1983                            final int userId = r.sbn.getUserId();
1984                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
1985                                    !mUserProfiles.isCurrentProfile(userId)) {
1986                                throw new SecurityException("Disallowed call from listener: "
1987                                        + info.service);
1988                            }
1989                            cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1990                                    r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1991                                    userId);
1992                        }
1993                    } else {
1994                        cancelAllLocked(callingUid, callingPid, info.userid,
1995                                REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1996                    }
1997                }
1998            } finally {
1999                Binder.restoreCallingIdentity(identity);
2000            }
2001        }
2002
2003        /**
2004         * Handle request from an approved listener to re-enable itself.
2005         *
2006         * @param component The componenet to be re-enabled, caller must match package.
2007         */
2008        @Override
2009        public void requestBindListener(ComponentName component) {
2010            checkCallerIsSystemOrSameApp(component.getPackageName());
2011            long identity = Binder.clearCallingIdentity();
2012            try {
2013                ManagedServices manager =
2014                        mNotificationAssistants.isComponentEnabledForCurrentProfiles(component)
2015                        ? mNotificationAssistants
2016                        : mListeners;
2017                manager.setComponentState(component, true);
2018            } finally {
2019                Binder.restoreCallingIdentity(identity);
2020            }
2021        }
2022
2023        @Override
2024        public void requestUnbindListener(INotificationListener token) {
2025            long identity = Binder.clearCallingIdentity();
2026            try {
2027                // allow bound services to disable themselves
2028                synchronized (mNotificationLock) {
2029                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2030                    info.getOwner().setComponentState(info.component, false);
2031                }
2032            } finally {
2033                Binder.restoreCallingIdentity(identity);
2034            }
2035        }
2036
2037        @Override
2038        public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2039            long identity = Binder.clearCallingIdentity();
2040            try {
2041                synchronized (mNotificationLock) {
2042                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2043                    if (keys != null) {
2044                        final int N = keys.length;
2045                        for (int i = 0; i < N; i++) {
2046                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
2047                            if (r == null) continue;
2048                            final int userId = r.sbn.getUserId();
2049                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
2050                                    !mUserProfiles.isCurrentProfile(userId)) {
2051                                throw new SecurityException("Disallowed call from listener: "
2052                                        + info.service);
2053                            }
2054                            if (!r.isSeen()) {
2055                                if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2056                                mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2057                                        userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2058                                                : userId,
2059                                        UsageEvents.Event.USER_INTERACTION);
2060                                r.setSeen();
2061                            }
2062                        }
2063                    }
2064                }
2065            } finally {
2066                Binder.restoreCallingIdentity(identity);
2067            }
2068        }
2069
2070        /**
2071         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2072         *
2073         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2074         *
2075         * @param info The binder for the listener, to check that the caller is allowed
2076         */
2077        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2078                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2079            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2080                    Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2081                    true,
2082                    userId, REASON_LISTENER_CANCEL, info);
2083        }
2084
2085        /**
2086         * Allow an INotificationListener to snooze a single notification until a context.
2087         *
2088         * @param token The binder for the listener, to check that the caller is allowed
2089         */
2090        @Override
2091        public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2092                String key, String snoozeCriterionId) {
2093            long identity = Binder.clearCallingIdentity();
2094            try {
2095                synchronized (mNotificationLock) {
2096                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2097                    snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2098                }
2099            } finally {
2100                Binder.restoreCallingIdentity(identity);
2101            }
2102        }
2103
2104        /**
2105         * Allow an INotificationListener to snooze a single notification until a time.
2106         *
2107         * @param token The binder for the listener, to check that the caller is allowed
2108         */
2109        @Override
2110        public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2111                long duration) {
2112            long identity = Binder.clearCallingIdentity();
2113            try {
2114                synchronized (mNotificationLock) {
2115                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2116                    snoozeNotificationInt(key, duration, null, info);
2117                }
2118            } finally {
2119                Binder.restoreCallingIdentity(identity);
2120            }
2121        }
2122
2123        /**
2124         * Allows the notification assistant to un-snooze a single notification.
2125         *
2126         * @param token The binder for the assistant, to check that the caller is allowed
2127         */
2128        @Override
2129        public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2130            long identity = Binder.clearCallingIdentity();
2131            try {
2132                synchronized (mNotificationLock) {
2133                    final ManagedServiceInfo info =
2134                            mNotificationAssistants.checkServiceTokenLocked(token);
2135                    unsnoozeNotificationInt(key, info);
2136                }
2137            } finally {
2138                Binder.restoreCallingIdentity(identity);
2139            }
2140        }
2141
2142        /**
2143         * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2144         *
2145         * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2146         *
2147         * @param token The binder for the listener, to check that the caller is allowed
2148         */
2149        @Override
2150        public void cancelNotificationFromListener(INotificationListener token, String pkg,
2151                String tag, int id) {
2152            final int callingUid = Binder.getCallingUid();
2153            final int callingPid = Binder.getCallingPid();
2154            long identity = Binder.clearCallingIdentity();
2155            try {
2156                synchronized (mNotificationLock) {
2157                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2158                    if (info.supportsProfiles()) {
2159                        Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2160                                + "from " + info.component
2161                                + " use cancelNotification(key) instead.");
2162                    } else {
2163                        cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2164                                pkg, tag, id, info.userid);
2165                    }
2166                }
2167            } finally {
2168                Binder.restoreCallingIdentity(identity);
2169            }
2170        }
2171
2172        /**
2173         * Allow an INotificationListener to request the list of outstanding notifications seen by
2174         * the current user. Useful when starting up, after which point the listener callbacks
2175         * should be used.
2176         *
2177         * @param token The binder for the listener, to check that the caller is allowed
2178         * @param keys An array of notification keys to fetch, or null to fetch everything
2179         * @returns The return value will contain the notifications specified in keys, in that
2180         *      order, or if keys is null, all the notifications, in natural order.
2181         */
2182        @Override
2183        public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2184                INotificationListener token, String[] keys, int trim) {
2185            synchronized (mNotificationLock) {
2186                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2187                final boolean getKeys = keys != null;
2188                final int N = getKeys ? keys.length : mNotificationList.size();
2189                final ArrayList<StatusBarNotification> list
2190                        = new ArrayList<StatusBarNotification>(N);
2191                for (int i=0; i<N; i++) {
2192                    final NotificationRecord r = getKeys
2193                            ? mNotificationsByKey.get(keys[i])
2194                            : mNotificationList.get(i);
2195                    if (r == null) continue;
2196                    StatusBarNotification sbn = r.sbn;
2197                    if (!isVisibleToListener(sbn, info)) continue;
2198                    StatusBarNotification sbnToSend =
2199                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2200                    list.add(sbnToSend);
2201                }
2202                return new ParceledListSlice<StatusBarNotification>(list);
2203            }
2204        }
2205
2206        /**
2207         * Allow an INotificationListener to request the list of outstanding snoozed notifications
2208         * seen by the current user. Useful when starting up, after which point the listener
2209         * callbacks should be used.
2210         *
2211         * @param token The binder for the listener, to check that the caller is allowed
2212         * @returns The return value will contain the notifications specified in keys, in that
2213         *      order, or if keys is null, all the notifications, in natural order.
2214         */
2215        @Override
2216        public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2217                INotificationListener token, int trim) {
2218            synchronized (mNotificationLock) {
2219                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2220                List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2221                final int N = snoozedRecords.size();
2222                final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2223                for (int i=0; i < N; i++) {
2224                    final NotificationRecord r = snoozedRecords.get(i);
2225                    if (r == null) continue;
2226                    StatusBarNotification sbn = r.sbn;
2227                    if (!isVisibleToListener(sbn, info)) continue;
2228                    StatusBarNotification sbnToSend =
2229                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2230                    list.add(sbnToSend);
2231                }
2232                return new ParceledListSlice<>(list);
2233            }
2234        }
2235
2236        @Override
2237        public void requestHintsFromListener(INotificationListener token, int hints) {
2238            final long identity = Binder.clearCallingIdentity();
2239            try {
2240                synchronized (mNotificationLock) {
2241                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2242                    final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2243                            | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2244                            | HINT_HOST_DISABLE_CALL_EFFECTS;
2245                    final boolean disableEffects = (hints & disableEffectsMask) != 0;
2246                    if (disableEffects) {
2247                        addDisabledHints(info, hints);
2248                    } else {
2249                        removeDisabledHints(info, hints);
2250                    }
2251                    updateListenerHintsLocked();
2252                    updateEffectsSuppressorLocked();
2253                }
2254            } finally {
2255                Binder.restoreCallingIdentity(identity);
2256            }
2257        }
2258
2259        @Override
2260        public int getHintsFromListener(INotificationListener token) {
2261            synchronized (mNotificationLock) {
2262                return mListenerHints;
2263            }
2264        }
2265
2266        @Override
2267        public void requestInterruptionFilterFromListener(INotificationListener token,
2268                int interruptionFilter) throws RemoteException {
2269            final long identity = Binder.clearCallingIdentity();
2270            try {
2271                synchronized (mNotificationLock) {
2272                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2273                    mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2274                    updateInterruptionFilterLocked();
2275                }
2276            } finally {
2277                Binder.restoreCallingIdentity(identity);
2278            }
2279        }
2280
2281        @Override
2282        public int getInterruptionFilterFromListener(INotificationListener token)
2283                throws RemoteException {
2284            synchronized (mNotificationLight) {
2285                return mInterruptionFilter;
2286            }
2287        }
2288
2289        @Override
2290        public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2291                throws RemoteException {
2292            synchronized (mNotificationLock) {
2293                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2294                if (info == null) return;
2295                mListeners.setOnNotificationPostedTrimLocked(info, trim);
2296            }
2297        }
2298
2299        @Override
2300        public int getZenMode() {
2301            return mZenModeHelper.getZenMode();
2302        }
2303
2304        @Override
2305        public ZenModeConfig getZenModeConfig() {
2306            enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2307            return mZenModeHelper.getConfig();
2308        }
2309
2310        @Override
2311        public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2312            enforceSystemOrSystemUI("INotificationManager.setZenMode");
2313            final long identity = Binder.clearCallingIdentity();
2314            try {
2315                mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2316            } finally {
2317                Binder.restoreCallingIdentity(identity);
2318            }
2319        }
2320
2321        @Override
2322        public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2323            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2324            return mZenModeHelper.getZenRules();
2325        }
2326
2327        @Override
2328        public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2329            Preconditions.checkNotNull(id, "Id is null");
2330            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2331            return mZenModeHelper.getAutomaticZenRule(id);
2332        }
2333
2334        @Override
2335        public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2336                throws RemoteException {
2337            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2338            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2339            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2340            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2341            enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2342
2343            return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2344                    "addAutomaticZenRule");
2345        }
2346
2347        @Override
2348        public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2349                throws RemoteException {
2350            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2351            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2352            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2353            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2354            enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2355
2356            return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2357                    "updateAutomaticZenRule");
2358        }
2359
2360        @Override
2361        public boolean removeAutomaticZenRule(String id) throws RemoteException {
2362            Preconditions.checkNotNull(id, "Id is null");
2363            // Verify that they can modify zen rules.
2364            enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2365
2366            return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2367        }
2368
2369        @Override
2370        public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2371            Preconditions.checkNotNull(packageName, "Package name is null");
2372            enforceSystemOrSystemUI("removeAutomaticZenRules");
2373
2374            return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2375        }
2376
2377        @Override
2378        public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2379            Preconditions.checkNotNull(owner, "Owner is null");
2380            enforceSystemOrSystemUI("getRuleInstanceCount");
2381
2382            return mZenModeHelper.getCurrentInstanceCount(owner);
2383        }
2384
2385        @Override
2386        public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2387            enforcePolicyAccess(pkg, "setInterruptionFilter");
2388            final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2389            if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2390            final long identity = Binder.clearCallingIdentity();
2391            try {
2392                mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2393            } finally {
2394                Binder.restoreCallingIdentity(identity);
2395            }
2396        }
2397
2398        @Override
2399        public void notifyConditions(final String pkg, IConditionProvider provider,
2400                final Condition[] conditions) {
2401            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2402            checkCallerIsSystemOrSameApp(pkg);
2403            mHandler.post(new Runnable() {
2404                @Override
2405                public void run() {
2406                    mConditionProviders.notifyConditions(pkg, info, conditions);
2407                }
2408            });
2409        }
2410
2411        @Override
2412        public void requestUnbindProvider(IConditionProvider provider) {
2413            long identity = Binder.clearCallingIdentity();
2414            try {
2415                // allow bound services to disable themselves
2416                final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2417                info.getOwner().setComponentState(info.component, false);
2418            } finally {
2419                Binder.restoreCallingIdentity(identity);
2420            }
2421        }
2422
2423        @Override
2424        public void requestBindProvider(ComponentName component) {
2425            checkCallerIsSystemOrSameApp(component.getPackageName());
2426            long identity = Binder.clearCallingIdentity();
2427            try {
2428                mConditionProviders.setComponentState(component, true);
2429            } finally {
2430                Binder.restoreCallingIdentity(identity);
2431            }
2432        }
2433
2434        private void enforceSystemOrSystemUI(String message) {
2435            if (isCallerSystemOrPhone()) return;
2436            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2437                    message);
2438        }
2439
2440        private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2441            try {
2442                checkCallerIsSystemOrSameApp(pkg);
2443            } catch (SecurityException e) {
2444                getContext().enforceCallingPermission(
2445                        android.Manifest.permission.STATUS_BAR_SERVICE,
2446                        message);
2447            }
2448        }
2449
2450        private void enforcePolicyAccess(int uid, String method) {
2451            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2452                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2453                return;
2454            }
2455            boolean accessAllowed = false;
2456            String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2457            final int packageCount = packages.length;
2458            for (int i = 0; i < packageCount; i++) {
2459                if (checkPolicyAccess(packages[i])) {
2460                    accessAllowed = true;
2461                }
2462            }
2463            if (!accessAllowed) {
2464                Slog.w(TAG, "Notification policy access denied calling " + method);
2465                throw new SecurityException("Notification policy access denied");
2466            }
2467        }
2468
2469        private void enforcePolicyAccess(String pkg, String method) {
2470            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2471                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2472                return;
2473            }
2474            checkCallerIsSameApp(pkg);
2475            if (!checkPolicyAccess(pkg)) {
2476                Slog.w(TAG, "Notification policy access denied calling " + method);
2477                throw new SecurityException("Notification policy access denied");
2478            }
2479        }
2480
2481        private boolean checkPackagePolicyAccess(String pkg) {
2482            return mPolicyAccess.isPackageGranted(pkg);
2483        }
2484
2485        private boolean checkPolicyAccess(String pkg) {
2486            try {
2487                int uid = getContext().getPackageManager().getPackageUidAsUser(
2488                        pkg, UserHandle.getCallingUserId());
2489                if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2490                        android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2491                        -1, true)) {
2492                    return true;
2493                }
2494            } catch (NameNotFoundException e) {
2495                return false;
2496            }
2497            return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2498        }
2499
2500        @Override
2501        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2502            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2503            final DumpFilter filter = DumpFilter.parseFromArguments(args);
2504            if (filter != null && filter.stats) {
2505                dumpJson(pw, filter);
2506            } else if (filter != null && filter.proto) {
2507                dumpProto(fd, filter);
2508            } else {
2509                dumpImpl(pw, filter);
2510            }
2511        }
2512
2513        @Override
2514        public ComponentName getEffectsSuppressor() {
2515            return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2516        }
2517
2518        @Override
2519        public boolean matchesCallFilter(Bundle extras) {
2520            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2521            return mZenModeHelper.matchesCallFilter(
2522                    Binder.getCallingUserHandle(),
2523                    extras,
2524                    mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2525                    MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2526                    MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2527        }
2528
2529        @Override
2530        public boolean isSystemConditionProviderEnabled(String path) {
2531            enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2532            return mConditionProviders.isSystemProviderEnabled(path);
2533        }
2534
2535        // Backup/restore interface
2536        @Override
2537        public byte[] getBackupPayload(int user) {
2538            if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2539            //TODO: http://b/22388012
2540            if (user != UserHandle.USER_SYSTEM) {
2541                Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2542                return null;
2543            }
2544            synchronized(mPolicyFile) {
2545                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2546                try {
2547                    writePolicyXml(baos, true /*forBackup*/);
2548                    return baos.toByteArray();
2549                } catch (IOException e) {
2550                    Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2551                }
2552            }
2553            return null;
2554        }
2555
2556        @Override
2557        public void applyRestore(byte[] payload, int user) {
2558            if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2559                    + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2560            if (payload == null) {
2561                Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2562                return;
2563            }
2564            //TODO: http://b/22388012
2565            if (user != UserHandle.USER_SYSTEM) {
2566                Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2567                return;
2568            }
2569            synchronized(mPolicyFile) {
2570                final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2571                try {
2572                    readPolicyXml(bais, true /*forRestore*/);
2573                    savePolicyFile();
2574                } catch (NumberFormatException | XmlPullParserException | IOException e) {
2575                    Slog.w(TAG, "applyRestore: error reading payload", e);
2576                }
2577            }
2578        }
2579
2580        @Override
2581        public boolean isNotificationPolicyAccessGranted(String pkg) {
2582            return checkPolicyAccess(pkg);
2583        }
2584
2585        @Override
2586        public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2587            enforceSystemOrSystemUIOrSamePackage(pkg,
2588                    "request policy access status for another package");
2589            return checkPolicyAccess(pkg);
2590        }
2591
2592        @Override
2593        public String[] getPackagesRequestingNotificationPolicyAccess()
2594                throws RemoteException {
2595            enforceSystemOrSystemUI("request policy access packages");
2596            final long identity = Binder.clearCallingIdentity();
2597            try {
2598                return mPolicyAccess.getRequestingPackages();
2599            } finally {
2600                Binder.restoreCallingIdentity(identity);
2601            }
2602        }
2603
2604        @Override
2605        public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2606                throws RemoteException {
2607            enforceSystemOrSystemUI("grant notification policy access");
2608            final long identity = Binder.clearCallingIdentity();
2609            try {
2610                synchronized (mNotificationLock) {
2611                    mPolicyAccess.put(pkg, granted);
2612                }
2613            } finally {
2614                Binder.restoreCallingIdentity(identity);
2615            }
2616        }
2617
2618        @Override
2619        public Policy getNotificationPolicy(String pkg) {
2620            enforcePolicyAccess(pkg, "getNotificationPolicy");
2621            final long identity = Binder.clearCallingIdentity();
2622            try {
2623                return mZenModeHelper.getNotificationPolicy();
2624            } finally {
2625                Binder.restoreCallingIdentity(identity);
2626            }
2627        }
2628
2629        @Override
2630        public void setNotificationPolicy(String pkg, Policy policy) {
2631            enforcePolicyAccess(pkg, "setNotificationPolicy");
2632            final long identity = Binder.clearCallingIdentity();
2633            try {
2634                mZenModeHelper.setNotificationPolicy(policy);
2635            } finally {
2636                Binder.restoreCallingIdentity(identity);
2637            }
2638        }
2639
2640        @Override
2641        public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2642                Adjustment adjustment) throws RemoteException {
2643            final long identity = Binder.clearCallingIdentity();
2644            try {
2645                synchronized (mNotificationLock) {
2646                    mNotificationAssistants.checkServiceTokenLocked(token);
2647                    int N = mEnqueuedNotifications.size();
2648                    for (int i = 0; i < N; i++) {
2649                        final NotificationRecord n = mEnqueuedNotifications.get(i);
2650                        if (Objects.equals(adjustment.getKey(), n.getKey())
2651                                && Objects.equals(adjustment.getUser(), n.getUserId())) {
2652                            applyAdjustment(n, adjustment);
2653                            break;
2654                        }
2655                    }
2656                }
2657            } finally {
2658                Binder.restoreCallingIdentity(identity);
2659            }
2660        }
2661
2662        @Override
2663        public void applyAdjustmentFromAssistant(INotificationListener token,
2664                Adjustment adjustment) throws RemoteException {
2665            final long identity = Binder.clearCallingIdentity();
2666            try {
2667                synchronized (mNotificationLock) {
2668                    mNotificationAssistants.checkServiceTokenLocked(token);
2669                    NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2670                    applyAdjustment(n, adjustment);
2671                }
2672                mRankingHandler.requestSort(true);
2673            } finally {
2674                Binder.restoreCallingIdentity(identity);
2675            }
2676        }
2677
2678        @Override
2679        public void applyAdjustmentsFromAssistant(INotificationListener token,
2680                List<Adjustment> adjustments) throws RemoteException {
2681
2682            final long identity = Binder.clearCallingIdentity();
2683            try {
2684                synchronized (mNotificationLock) {
2685                    mNotificationAssistants.checkServiceTokenLocked(token);
2686                    for (Adjustment adjustment : adjustments) {
2687                        NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2688                        applyAdjustment(n, adjustment);
2689                    }
2690                }
2691                mRankingHandler.requestSort(true);
2692            } finally {
2693                Binder.restoreCallingIdentity(identity);
2694            }
2695        }
2696
2697        @Override
2698        public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2699                String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2700            Preconditions.checkNotNull(channel);
2701            Preconditions.checkNotNull(pkg);
2702            Preconditions.checkNotNull(user);
2703
2704            verifyPrivilegedListener(token, user);
2705            updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2706        }
2707
2708        @Override
2709        public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2710                INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2711            Preconditions.checkNotNull(pkg);
2712            Preconditions.checkNotNull(user);
2713            verifyPrivilegedListener(token, user);
2714
2715            return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2716                    false /* includeDeleted */);
2717        }
2718
2719        @Override
2720        public ParceledListSlice<NotificationChannelGroup>
2721                getNotificationChannelGroupsFromPrivilegedListener(
2722                INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2723            Preconditions.checkNotNull(pkg);
2724            Preconditions.checkNotNull(user);
2725            verifyPrivilegedListener(token, user);
2726
2727            List<NotificationChannelGroup> groups = new ArrayList<>();
2728            groups.addAll(mRankingHelper.getNotificationChannelGroups(
2729                    pkg, getUidForPackageAndUser(pkg, user)));
2730            return new ParceledListSlice<>(groups);
2731        }
2732
2733        private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
2734            ManagedServiceInfo info;
2735            synchronized (mNotificationLock) {
2736                info = mListeners.checkServiceTokenLocked(token);
2737            }
2738            if (!hasCompanionDevice(info)) {
2739                throw new SecurityException(info + " does not have access");
2740            }
2741            if (!info.enabledAndUserMatches(user.getIdentifier())) {
2742                throw new SecurityException(info + " does not have access");
2743            }
2744        }
2745
2746        private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2747            int uid = 0;
2748            long identity = Binder.clearCallingIdentity();
2749            try {
2750                uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2751            } finally {
2752                Binder.restoreCallingIdentity(identity);
2753            }
2754            return uid;
2755        }
2756    };
2757
2758    private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
2759        if (n == null) {
2760            return;
2761        }
2762        if (adjustment.getSignals() != null) {
2763            Bundle.setDefusable(adjustment.getSignals(), true);
2764            final ArrayList<String> people =
2765                    adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
2766            final ArrayList<SnoozeCriterion> snoozeCriterionList =
2767                    adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
2768            n.setPeopleOverride(people);
2769            n.setSnoozeCriteria(snoozeCriterionList);
2770        }
2771    }
2772
2773    private void addAutogroupKeyLocked(String key) {
2774        NotificationRecord n = mNotificationsByKey.get(key);
2775        if (n == null) {
2776            return;
2777        }
2778        n.sbn.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
2779        EventLogTags.writeNotificationAutogrouped(key);
2780    }
2781
2782    private void removeAutogroupKeyLocked(String key) {
2783        NotificationRecord n = mNotificationsByKey.get(key);
2784        if (n == null) {
2785            return;
2786        }
2787        n.sbn.setOverrideGroupKey(null);
2788        EventLogTags.writeNotificationUnautogrouped(key);
2789    }
2790
2791    // Clears the 'fake' auto-group summary.
2792    private void clearAutogroupSummaryLocked(int userId, String pkg) {
2793        ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2794        if (summaries != null && summaries.containsKey(pkg)) {
2795            // Clear summary.
2796            final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
2797            if (removed != null) {
2798                cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2799            }
2800        }
2801    }
2802
2803    // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2804    private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
2805        NotificationRecord summaryRecord = null;
2806        synchronized (mNotificationLock) {
2807            NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
2808            if (notificationRecord == null) {
2809                // The notification could have been cancelled again already. A successive
2810                // adjustment will post a summary if needed.
2811                return;
2812            }
2813            final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2814            userId = adjustedSbn.getUser().getIdentifier();
2815            ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2816            if (summaries == null) {
2817                summaries = new ArrayMap<>();
2818            }
2819            mAutobundledSummaries.put(userId, summaries);
2820            if (!summaries.containsKey(pkg)) {
2821                // Add summary
2822                final ApplicationInfo appInfo =
2823                       adjustedSbn.getNotification().extras.getParcelable(
2824                               Notification.EXTRA_BUILDER_APPLICATION_INFO);
2825                final Bundle extras = new Bundle();
2826                extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2827                final String channelId = notificationRecord.getChannel().getId();
2828                final Notification summaryNotification =
2829                        new Notification.Builder(getContext(), channelId)
2830                                .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
2831                                .setGroupSummary(true)
2832                                .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
2833                                .setGroup(GroupHelper.AUTOGROUP_KEY)
2834                                .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2835                                .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2836                                .setColor(adjustedSbn.getNotification().color)
2837                                .setLocalOnly(true)
2838                                .build();
2839                summaryNotification.extras.putAll(extras);
2840                Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
2841                if (appIntent != null) {
2842                    summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2843                            getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
2844                }
2845                final StatusBarNotification summarySbn =
2846                        new StatusBarNotification(adjustedSbn.getPackageName(),
2847                                adjustedSbn.getOpPkg(),
2848                                Integer.MAX_VALUE,
2849                                GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
2850                                adjustedSbn.getInitialPid(), summaryNotification,
2851                                adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
2852                                System.currentTimeMillis());
2853                summaryRecord = new NotificationRecord(getContext(), summarySbn,
2854                        notificationRecord.getChannel());
2855                summaries.put(pkg, summarySbn.getKey());
2856            }
2857        }
2858        if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
2859                summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
2860            mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2861        }
2862    }
2863
2864    private String disableNotificationEffects(NotificationRecord record) {
2865        if (mDisableNotificationEffects) {
2866            return "booleanState";
2867        }
2868        if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2869            return "listenerHints";
2870        }
2871        if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2872            return "callState";
2873        }
2874        return null;
2875    };
2876
2877    private void dumpJson(PrintWriter pw, DumpFilter filter) {
2878        JSONObject dump = new JSONObject();
2879        try {
2880            dump.put("service", "Notification Manager");
2881            dump.put("bans", mRankingHelper.dumpBansJson(filter));
2882            dump.put("ranking", mRankingHelper.dumpJson(filter));
2883            dump.put("stats", mUsageStats.dumpJson(filter));
2884            dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
2885        } catch (JSONException e) {
2886            e.printStackTrace();
2887        }
2888        pw.println(dump);
2889    }
2890
2891    private void dumpProto(FileDescriptor fd, DumpFilter filter) {
2892        final ProtoOutputStream proto = new ProtoOutputStream(fd);
2893        synchronized (mNotificationLock) {
2894            long records = proto.start(NotificationServiceDumpProto.RECORDS);
2895            int N = mNotificationList.size();
2896            if (N > 0) {
2897                for (int i = 0; i < N; i++) {
2898                    final NotificationRecord nr = mNotificationList.get(i);
2899                    if (filter.filtered && !filter.matches(nr.sbn)) continue;
2900                    nr.dump(proto, filter.redact);
2901                    proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
2902                }
2903            }
2904            N = mEnqueuedNotifications.size();
2905            if (N > 0) {
2906                for (int i = 0; i < N; i++) {
2907                    final NotificationRecord nr = mEnqueuedNotifications.get(i);
2908                    if (filter.filtered && !filter.matches(nr.sbn)) continue;
2909                    nr.dump(proto, filter.redact);
2910                    proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
2911                }
2912            }
2913            List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
2914            N = snoozed.size();
2915            if (N > 0) {
2916                for (int i = 0; i < N; i++) {
2917                    final NotificationRecord nr = snoozed.get(i);
2918                    if (filter.filtered && !filter.matches(nr.sbn)) continue;
2919                    nr.dump(proto, filter.redact);
2920                    proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
2921                }
2922            }
2923            proto.end(records);
2924        }
2925
2926        long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
2927        mZenModeHelper.dump(proto);
2928        for (ComponentName suppressor : mEffectsSuppressors) {
2929            proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
2930        }
2931        proto.end(zenLog);
2932
2933        proto.flush();
2934    }
2935
2936    void dumpImpl(PrintWriter pw, DumpFilter filter) {
2937        pw.print("Current Notification Manager state");
2938        if (filter.filtered) {
2939            pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2940        }
2941        pw.println(':');
2942        int N;
2943        final boolean zenOnly = filter.filtered && filter.zen;
2944
2945        if (!zenOnly) {
2946            synchronized (mToastQueue) {
2947                N = mToastQueue.size();
2948                if (N > 0) {
2949                    pw.println("  Toast Queue:");
2950                    for (int i=0; i<N; i++) {
2951                        mToastQueue.get(i).dump(pw, "    ", filter);
2952                    }
2953                    pw.println("  ");
2954                }
2955            }
2956        }
2957
2958        synchronized (mNotificationLock) {
2959            if (!zenOnly) {
2960                N = mNotificationList.size();
2961                if (N > 0) {
2962                    pw.println("  Notification List:");
2963                    for (int i=0; i<N; i++) {
2964                        final NotificationRecord nr = mNotificationList.get(i);
2965                        if (filter.filtered && !filter.matches(nr.sbn)) continue;
2966                        nr.dump(pw, "    ", getContext(), filter.redact);
2967                    }
2968                    pw.println("  ");
2969                }
2970
2971                if (!filter.filtered) {
2972                    N = mLights.size();
2973                    if (N > 0) {
2974                        pw.println("  Lights List:");
2975                        for (int i=0; i<N; i++) {
2976                            if (i == N - 1) {
2977                                pw.print("  > ");
2978                            } else {
2979                                pw.print("    ");
2980                            }
2981                            pw.println(mLights.get(i));
2982                        }
2983                        pw.println("  ");
2984                    }
2985                    pw.println("  mUseAttentionLight=" + mUseAttentionLight);
2986                    pw.println("  mNotificationPulseEnabled=" + mNotificationPulseEnabled);
2987                    pw.println("  mSoundNotificationKey=" + mSoundNotificationKey);
2988                    pw.println("  mVibrateNotificationKey=" + mVibrateNotificationKey);
2989                    pw.println("  mDisableNotificationEffects=" + mDisableNotificationEffects);
2990                    pw.println("  mCallState=" + callStateToString(mCallState));
2991                    pw.println("  mSystemReady=" + mSystemReady);
2992                    pw.println("  mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
2993                }
2994                pw.println("  mArchive=" + mArchive.toString());
2995                Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2996                int j=0;
2997                while (iter.hasNext()) {
2998                    final StatusBarNotification sbn = iter.next();
2999                    if (filter != null && !filter.matches(sbn)) continue;
3000                    pw.println("    " + sbn);
3001                    if (++j >= 5) {
3002                        if (iter.hasNext()) pw.println("    ...");
3003                        break;
3004                    }
3005                }
3006
3007                if (!zenOnly) {
3008                    N = mEnqueuedNotifications.size();
3009                    if (N > 0) {
3010                        pw.println("  Enqueued Notification List:");
3011                        for (int i = 0; i < N; i++) {
3012                            final NotificationRecord nr = mEnqueuedNotifications.get(i);
3013                            if (filter.filtered && !filter.matches(nr.sbn)) continue;
3014                            nr.dump(pw, "    ", getContext(), filter.redact);
3015                        }
3016                        pw.println("  ");
3017                    }
3018
3019                    mSnoozeHelper.dump(pw, filter);
3020                }
3021            }
3022
3023            if (!zenOnly) {
3024                pw.println("\n  Ranking Config:");
3025                mRankingHelper.dump(pw, "    ", filter);
3026
3027                pw.println("\n  Notification listeners:");
3028                mListeners.dump(pw, filter);
3029                pw.print("    mListenerHints: "); pw.println(mListenerHints);
3030                pw.print("    mListenersDisablingEffects: (");
3031                N = mListenersDisablingEffects.size();
3032                for (int i = 0; i < N; i++) {
3033                    final int hint = mListenersDisablingEffects.keyAt(i);
3034                    if (i > 0) pw.print(';');
3035                    pw.print("hint[" + hint + "]:");
3036
3037                    final ArraySet<ManagedServiceInfo> listeners =
3038                            mListenersDisablingEffects.valueAt(i);
3039                    final int listenerSize = listeners.size();
3040
3041                    for (int j = 0; j < listenerSize; j++) {
3042                        if (i > 0) pw.print(',');
3043                        final ManagedServiceInfo listener = listeners.valueAt(i);
3044                        pw.print(listener.component);
3045                    }
3046                }
3047                pw.println(')');
3048                pw.println("\n  Notification assistant services:");
3049                mNotificationAssistants.dump(pw, filter);
3050            }
3051
3052            if (!filter.filtered || zenOnly) {
3053                pw.println("\n  Zen Mode:");
3054                pw.print("    mInterruptionFilter="); pw.println(mInterruptionFilter);
3055                mZenModeHelper.dump(pw, "    ");
3056
3057                pw.println("\n  Zen Log:");
3058                ZenLog.dump(pw, "    ");
3059            }
3060
3061            pw.println("\n  Policy access:");
3062            pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
3063
3064            pw.println("\n  Condition providers:");
3065            mConditionProviders.dump(pw, filter);
3066
3067            pw.println("\n  Group summaries:");
3068            for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3069                NotificationRecord r = entry.getValue();
3070                pw.println("    " + entry.getKey() + " -> " + r.getKey());
3071                if (mNotificationsByKey.get(r.getKey()) != r) {
3072                    pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3073                    r.dump(pw, "      ", getContext(), filter.redact);
3074                }
3075            }
3076
3077            if (!zenOnly) {
3078                pw.println("\n  Usage Stats:");
3079                mUsageStats.dump(pw, "    ", filter);
3080            }
3081        }
3082    }
3083
3084    /**
3085     * The private API only accessible to the system process.
3086     */
3087    private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3088        @Override
3089        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3090                String tag, int id, Notification notification, int userId) {
3091            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3092                    userId);
3093        }
3094
3095        @Override
3096        public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3097                int userId) {
3098            checkCallerIsSystem();
3099            mHandler.post(new Runnable() {
3100                @Override
3101                public void run() {
3102                    synchronized (mNotificationLock) {
3103                        removeForegroundServiceFlagByListLocked(
3104                                mEnqueuedNotifications, pkg, notificationId, userId);
3105                        removeForegroundServiceFlagByListLocked(
3106                                mNotificationList, pkg, notificationId, userId);
3107                    }
3108                }
3109            });
3110        }
3111
3112        private void removeForegroundServiceFlagByListLocked(
3113                ArrayList<NotificationRecord> notificationList, String pkg, int notificationId, int userId) {
3114            NotificationRecord r =
3115                    findNotificationByListLocked(notificationList, pkg, null, notificationId, userId);
3116            if (r == null) {
3117                return;
3118            }
3119            StatusBarNotification sbn = r.sbn;
3120            // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3121            // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3122            // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3123            // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3124            sbn.getNotification().flags =
3125                    (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3126            mRankingHelper.sort(mNotificationList);
3127            mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3128            mGroupHelper.onNotificationPosted(sbn);
3129        }
3130    };
3131
3132    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3133            final int callingPid, final String tag, final int id, final Notification notification,
3134            int incomingUserId) {
3135        if (DBG) {
3136            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3137                    + " notification=" + notification);
3138        }
3139        checkCallerIsSystemOrSameApp(pkg);
3140
3141        final int userId = ActivityManager.handleIncomingUser(callingPid,
3142                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3143        final UserHandle user = new UserHandle(userId);
3144
3145        if (pkg == null || notification == null) {
3146            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3147                    + " id=" + id + " notification=" + notification);
3148        }
3149
3150        // The system can post notifications for any package, let us resolve that.
3151        final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3152
3153        // Fix the notification as best we can.
3154        try {
3155            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3156                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3157                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3158            Notification.addFieldsFromContext(ai, notification);
3159        } catch (NameNotFoundException e) {
3160            Slog.e(TAG, "Cannot create a context for sending app", e);
3161            return;
3162        }
3163
3164        mUsageStats.registerEnqueuedByApp(pkg);
3165
3166        // setup local book-keeping
3167        String channelId = notification.getChannelId();
3168        if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3169            channelId = (new Notification.TvExtender(notification)).getChannelId();
3170        }
3171        final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3172                notificationUid, channelId, false /* includeDeleted */);
3173        if (channel == null) {
3174            final String noChannelStr = "No Channel found for "
3175                    + "pkg=" + pkg
3176                    + ", channelId=" + channelId
3177                    + ", id=" + id
3178                    + ", tag=" + tag
3179                    + ", opPkg=" + opPkg
3180                    + ", callingUid=" + callingUid
3181                    + ", userId=" + userId
3182                    + ", incomingUserId=" + incomingUserId
3183                    + ", notificationUid=" + notificationUid
3184                    + ", notification=" + notification;
3185            Log.e(TAG, noChannelStr);
3186            doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3187                    "Failed to post notification on channel \"" + channelId + "\"\n" +
3188                    "See log for more details");
3189            return;
3190        }
3191
3192        final StatusBarNotification n = new StatusBarNotification(
3193                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3194                user, null, System.currentTimeMillis());
3195        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3196
3197        if (!checkDisqualifyingFeatures(userId, notificationUid, id,tag, r)) {
3198            return;
3199        }
3200
3201        // Whitelist pending intents.
3202        if (notification.allPendingIntents != null) {
3203            final int intentCount = notification.allPendingIntents.size();
3204            if (intentCount > 0) {
3205                final ActivityManagerInternal am = LocalServices
3206                        .getService(ActivityManagerInternal.class);
3207                final long duration = LocalServices.getService(
3208                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3209                for (int i = 0; i < intentCount; i++) {
3210                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3211                    if (pendingIntent != null) {
3212                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3213                                WHITELIST_TOKEN, duration);
3214                    }
3215                }
3216            }
3217        }
3218
3219        mHandler.post(new EnqueueNotificationRunnable(userId, r));
3220    }
3221
3222    private void doChannelWarningToast(CharSequence toastText) {
3223        final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3224        final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3225                Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3226        if (warningEnabled) {
3227            Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3228                    Toast.LENGTH_SHORT);
3229            toast.show();
3230        }
3231    }
3232
3233    private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3234        // The system can post notifications on behalf of any package it wants
3235        if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3236            try {
3237                return getContext().getPackageManager()
3238                        .getPackageUidAsUser(opPackageName, userId);
3239            } catch (NameNotFoundException e) {
3240                /* ignore */
3241            }
3242        }
3243        return callingUid;
3244    }
3245
3246    /**
3247     * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3248     *
3249     * Has side effects.
3250     */
3251    private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3252            NotificationRecord r) {
3253        final String pkg = r.sbn.getPackageName();
3254        final boolean isSystemNotification =
3255                isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
3256        final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3257
3258        // Limit the number of notifications that any given package except the android
3259        // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
3260        if (!isSystemNotification && !isNotificationFromListener) {
3261            synchronized (mNotificationLock) {
3262                if (mNotificationsByKey.get(r.sbn.getKey()) != null) {
3263                    // this is an update, rate limit updates only
3264                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3265                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
3266                        mUsageStats.registerOverRateQuota(pkg);
3267                        final long now = SystemClock.elapsedRealtime();
3268                        if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3269                            Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3270                                    + ". Shedding events. package=" + pkg);
3271                            mLastOverRateLogTime = now;
3272                        }
3273                        return false;
3274                    }
3275                } else if (isCallerInstantApp(pkg)) {
3276                    // Ephemeral apps have some special constraints for notifications.
3277                    // They are not allowed to create new notifications however they are allowed to
3278                    // update notifications created by the system (e.g. a foreground service
3279                    // notification).
3280                    throw new SecurityException("Instant app " + pkg
3281                            + " cannot create notifications");
3282                }
3283
3284                int count = 0;
3285                final int N = mNotificationList.size();
3286                for (int i=0; i<N; i++) {
3287                    final NotificationRecord existing = mNotificationList.get(i);
3288                    if (existing.sbn.getPackageName().equals(pkg)
3289                            && existing.sbn.getUserId() == userId) {
3290                        if (existing.sbn.getId() == id
3291                                && TextUtils.equals(existing.sbn.getTag(), tag)) {
3292                            break;  // Allow updating existing notification
3293                        }
3294                        count++;
3295                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3296                            mUsageStats.registerOverCountQuota(pkg);
3297                            Slog.e(TAG, "Package has already posted " + count
3298                                    + " notifications.  Not showing more.  package=" + pkg);
3299                            return false;
3300                        }
3301                    }
3302                }
3303            }
3304        }
3305
3306        // snoozed apps
3307        if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3308            MetricsLogger.action(r.getLogMaker()
3309                    .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3310                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3311            if (DBG) {
3312                Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3313            }
3314            mSnoozeHelper.update(userId, r);
3315            savePolicyFile();
3316            return false;
3317        }
3318
3319
3320        // blocked apps
3321        if (isBlocked(r, mUsageStats)) {
3322            return false;
3323        }
3324
3325        return true;
3326    }
3327
3328    protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3329        final String pkg = r.sbn.getPackageName();
3330        final int callingUid = r.sbn.getUid();
3331
3332        final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3333        if (isPackageSuspended) {
3334            Slog.e(TAG, "Suppressing notification from package due to package "
3335                    + "suspended by administrator.");
3336            usageStats.registerSuspendedByAdmin(r);
3337            return isPackageSuspended;
3338        }
3339
3340        final boolean isBlocked = r.getImportance() == NotificationManager.IMPORTANCE_NONE
3341                || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3342        if (isBlocked) {
3343            Slog.e(TAG, "Suppressing notification from package by user request.");
3344            usageStats.registerBlocked(r);
3345        }
3346        return isBlocked;
3347    }
3348
3349    protected class SnoozeNotificationRunnable implements Runnable {
3350        private final String mKey;
3351        private final long mDuration;
3352        private final String mSnoozeCriterionId;
3353
3354        SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3355            mKey = key;
3356            mDuration = duration;
3357            mSnoozeCriterionId = snoozeCriterionId;
3358        }
3359
3360        @Override
3361        public void run() {
3362            synchronized (mNotificationLock) {
3363                final NotificationRecord r = findNotificationByKeyLocked(mKey);
3364                if (r != null) {
3365                    snoozeLocked(r);
3366                }
3367            }
3368        }
3369
3370        void snoozeLocked(NotificationRecord r) {
3371            if (r.sbn.isGroup()) {
3372                final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3373                        r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3374                if (r.getNotification().isGroupSummary()) {
3375                    // snooze summary and all children
3376                    for (int i = 0; i < groupNotifications.size(); i++) {
3377                        snoozeNotificationLocked(groupNotifications.get(i));
3378                    }
3379                } else {
3380                    // if there is a valid summary for this group, and we are snoozing the only
3381                    // child, also snooze the summary
3382                    if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3383                        if (groupNotifications.size() != 2) {
3384                            snoozeNotificationLocked(r);
3385                        } else {
3386                            // snooze summary and the one child
3387                            for (int i = 0; i < groupNotifications.size(); i++) {
3388                                snoozeNotificationLocked(groupNotifications.get(i));
3389                            }
3390                        }
3391                    } else {
3392                        snoozeNotificationLocked(r);
3393                    }
3394                }
3395            } else {
3396                // just snooze the one notification
3397                snoozeNotificationLocked(r);
3398            }
3399        }
3400
3401        void snoozeNotificationLocked(NotificationRecord r) {
3402            MetricsLogger.action(r.getLogMaker()
3403                    .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3404                    .setType(MetricsEvent.TYPE_CLOSE)
3405                    .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3406                            mSnoozeCriterionId == null ? 0 : 1));
3407            cancelNotificationLocked(r, false, REASON_SNOOZED);
3408            updateLightsLocked();
3409            if (mSnoozeCriterionId != null) {
3410                mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3411                mSnoozeHelper.snooze(r);
3412            } else {
3413                mSnoozeHelper.snooze(r, mDuration);
3414            }
3415            savePolicyFile();
3416        }
3417    }
3418
3419    protected class EnqueueNotificationRunnable implements Runnable {
3420        private final NotificationRecord r;
3421        private final int userId;
3422
3423        EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3424            this.userId = userId;
3425            this.r = r;
3426        };
3427
3428        @Override
3429        public void run() {
3430            synchronized (mNotificationLock) {
3431                mEnqueuedNotifications.add(r);
3432                scheduleTimeoutLocked(r);
3433
3434                final StatusBarNotification n = r.sbn;
3435                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3436                NotificationRecord old = mNotificationsByKey.get(n.getKey());
3437                if (old != null) {
3438                    // Retain ranking information from previous record
3439                    r.copyRankingInformation(old);
3440                }
3441
3442                final int callingUid = n.getUid();
3443                final int callingPid = n.getInitialPid();
3444                final Notification notification = n.getNotification();
3445                final String pkg = n.getPackageName();
3446                final int id = n.getId();
3447                final String tag = n.getTag();
3448
3449                // Handle grouped notifications and bail out early if we
3450                // can to avoid extracting signals.
3451                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3452
3453                // if this is a group child, unsnooze parent summary
3454                if (n.isGroup() && notification.isGroupChild()) {
3455                    mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3456                }
3457
3458                // This conditional is a dirty hack to limit the logging done on
3459                //     behalf of the download manager without affecting other apps.
3460                if (!pkg.equals("com.android.providers.downloads")
3461                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3462                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3463                    if (old != null) {
3464                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3465                    }
3466                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3467                            pkg, id, tag, userId, notification.toString(),
3468                            enqueueStatus);
3469                }
3470
3471                mRankingHelper.extractSignals(r);
3472
3473                // tell the assistant service about the notification
3474                if (mNotificationAssistants.isEnabled()) {
3475                    mNotificationAssistants.onNotificationEnqueued(r);
3476                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3477                            DELAY_FOR_ASSISTANT_TIME);
3478                } else {
3479                    mHandler.post(new PostNotificationRunnable(r.getKey()));
3480                }
3481            }
3482        }
3483    }
3484
3485    protected class PostNotificationRunnable implements Runnable {
3486        private final String key;
3487
3488        PostNotificationRunnable(String key) {
3489            this.key = key;
3490        }
3491
3492        @Override
3493        public void run() {
3494            synchronized (mNotificationLock) {
3495                try {
3496                    NotificationRecord r = null;
3497                    int N = mEnqueuedNotifications.size();
3498                    for (int i = 0; i < N; i++) {
3499                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3500                        if (Objects.equals(key, enqueued.getKey())) {
3501                            r = enqueued;
3502                            break;
3503                        }
3504                    }
3505                    if (r == null) {
3506                        Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3507                        return;
3508                    }
3509                    NotificationRecord old = mNotificationsByKey.get(key);
3510                    final StatusBarNotification n = r.sbn;
3511                    final Notification notification = n.getNotification();
3512                    int index = indexOfNotificationLocked(n.getKey());
3513                    if (index < 0) {
3514                        mNotificationList.add(r);
3515                        mUsageStats.registerPostedByApp(r);
3516                    } else {
3517                        old = mNotificationList.get(index);
3518                        mNotificationList.set(index, r);
3519                        mUsageStats.registerUpdatedByApp(r, old);
3520                        // Make sure we don't lose the foreground service state.
3521                        notification.flags |=
3522                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3523                        r.isUpdate = true;
3524                    }
3525
3526                    mNotificationsByKey.put(n.getKey(), r);
3527
3528                    // Ensure if this is a foreground service that the proper additional
3529                    // flags are set.
3530                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3531                        notification.flags |= Notification.FLAG_ONGOING_EVENT
3532                                | Notification.FLAG_NO_CLEAR;
3533                    }
3534
3535                    applyZenModeLocked(r);
3536                    mRankingHelper.sort(mNotificationList);
3537
3538                    if (notification.getSmallIcon() != null) {
3539                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3540                        mListeners.notifyPostedLocked(n, oldSbn);
3541                        mHandler.post(new Runnable() {
3542                            @Override
3543                            public void run() {
3544                                mGroupHelper.onNotificationPosted(n);
3545                            }
3546                        });
3547                    } else {
3548                        Slog.e(TAG, "Not posting notification without small icon: " + notification);
3549                        if (old != null && !old.isCanceled) {
3550                            mListeners.notifyRemovedLocked(n,
3551                                    NotificationListenerService.REASON_ERROR);
3552                            mHandler.post(new Runnable() {
3553                                @Override
3554                                public void run() {
3555                                    mGroupHelper.onNotificationRemoved(n);
3556                                }
3557                            });
3558                        }
3559                        // ATTENTION: in a future release we will bail out here
3560                        // so that we do not play sounds, show lights, etc. for invalid
3561                        // notifications
3562                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3563                                + n.getPackageName());
3564                    }
3565
3566                    buzzBeepBlinkLocked(r);
3567                } finally {
3568                    int N = mEnqueuedNotifications.size();
3569                    for (int i = 0; i < N; i++) {
3570                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3571                        if (Objects.equals(key, enqueued.getKey())) {
3572                            mEnqueuedNotifications.remove(i);
3573                            break;
3574                        }
3575                    }
3576                }
3577            }
3578        }
3579    }
3580
3581    /**
3582     * Ensures that grouped notification receive their special treatment.
3583     *
3584     * <p>Cancels group children if the new notification causes a group to lose
3585     * its summary.</p>
3586     *
3587     * <p>Updates mSummaryByGroupKey.</p>
3588     */
3589    private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3590            int callingUid, int callingPid) {
3591        StatusBarNotification sbn = r.sbn;
3592        Notification n = sbn.getNotification();
3593        if (n.isGroupSummary() && !sbn.isAppGroup())  {
3594            // notifications without a group shouldn't be a summary, otherwise autobundling can
3595            // lead to bugs
3596            n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3597        }
3598
3599        String group = sbn.getGroupKey();
3600        boolean isSummary = n.isGroupSummary();
3601
3602        Notification oldN = old != null ? old.sbn.getNotification() : null;
3603        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3604        boolean oldIsSummary = old != null && oldN.isGroupSummary();
3605
3606        if (oldIsSummary) {
3607            NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3608            if (removedSummary != old) {
3609                String removedKey =
3610                        removedSummary != null ? removedSummary.getKey() : "<null>";
3611                Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3612                        ", removed=" + removedKey);
3613            }
3614        }
3615        if (isSummary) {
3616            mSummaryByGroupKey.put(group, r);
3617        }
3618
3619        // Clear out group children of the old notification if the update
3620        // causes the group summary to go away. This happens when the old
3621        // notification was a summary and the new one isn't, or when the old
3622        // notification was a summary and its group key changed.
3623        if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3624            cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
3625        }
3626    }
3627
3628    @VisibleForTesting
3629    void scheduleTimeoutLocked(NotificationRecord record) {
3630        if (record.getNotification().getTimeoutAfter() > 0) {
3631            final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3632                    REQUEST_CODE_TIMEOUT,
3633                    new Intent(ACTION_NOTIFICATION_TIMEOUT)
3634                            .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3635                                    .appendPath(record.getKey()).build())
3636                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3637                            .putExtra(EXTRA_KEY, record.getKey()),
3638                    PendingIntent.FLAG_UPDATE_CURRENT);
3639            mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3640                    SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
3641        }
3642    }
3643
3644    @VisibleForTesting
3645    void buzzBeepBlinkLocked(NotificationRecord record) {
3646        boolean buzz = false;
3647        boolean beep = false;
3648        boolean blink = false;
3649
3650        final Notification notification = record.sbn.getNotification();
3651        final String key = record.getKey();
3652
3653        // Should this notification make noise, vibe, or use the LED?
3654        final boolean aboveThreshold =
3655                record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
3656        final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
3657        if (DBG)
3658            Slog.v(TAG,
3659                    "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
3660                            " intercept=" + record.isIntercepted()
3661            );
3662
3663        // If we're not supposed to beep, vibrate, etc. then don't.
3664        final String disableEffects = disableNotificationEffects(record);
3665        if (disableEffects != null) {
3666            ZenLog.traceDisableEffects(record, disableEffects);
3667        }
3668
3669        // Remember if this notification already owns the notification channels.
3670        boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3671        boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
3672        // These are set inside the conditional if the notification is allowed to make noise.
3673        boolean hasValidVibrate = false;
3674        boolean hasValidSound = false;
3675
3676        if (isNotificationForCurrentUser(record)) {
3677            // If the notification will appear in the status bar, it should send an accessibility
3678            // event
3679            if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
3680                sendAccessibilityEvent(notification, record.sbn.getPackageName());
3681            }
3682
3683            if (disableEffects == null
3684                    && canInterrupt
3685                    && mSystemReady
3686                    && mAudioManager != null) {
3687                if (DBG) Slog.v(TAG, "Interrupting!");
3688                Uri soundUri = record.getSound();
3689                hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
3690                long[] vibration = record.getVibration();
3691                // Demote sound to vibration if vibration missing & phone in vibration mode.
3692                if (vibration == null
3693                        && hasValidSound
3694                        && (mAudioManager.getRingerModeInternal()
3695                        == AudioManager.RINGER_MODE_VIBRATE)) {
3696                    vibration = mFallbackVibrationPattern;
3697                }
3698                hasValidVibrate = vibration != null;
3699
3700                if (!shouldMuteNotificationLocked(record)) {
3701                    if (hasValidSound) {
3702                        mSoundNotificationKey = key;
3703                        if (mInCall) {
3704                            playInCallNotification();
3705                            beep = true;
3706                        } else {
3707                            beep = playSound(record, soundUri);
3708                        }
3709                    }
3710
3711                    final boolean ringerModeSilent =
3712                            mAudioManager.getRingerModeInternal()
3713                                    == AudioManager.RINGER_MODE_SILENT;
3714                    if (!mInCall && hasValidVibrate && !ringerModeSilent) {
3715                        mVibrateNotificationKey = key;
3716
3717                        buzz = playVibration(record, vibration);
3718                    }
3719                }
3720            }
3721        }
3722        // If a notification is updated to remove the actively playing sound or vibrate,
3723        // cancel that feedback now
3724        if (wasBeep && !hasValidSound) {
3725            clearSoundLocked();
3726        }
3727        if (wasBuzz && !hasValidVibrate) {
3728            clearVibrateLocked();
3729        }
3730
3731        // light
3732        // release the light
3733        boolean wasShowLights = mLights.remove(key);
3734        if (record.getLight() != null && aboveThreshold
3735                && ((record.getSuppressedVisualEffects()
3736                & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
3737            mLights.add(key);
3738            updateLightsLocked();
3739            if (mUseAttentionLight) {
3740                mAttentionLight.pulse();
3741            }
3742            blink = true;
3743        } else if (wasShowLights) {
3744            updateLightsLocked();
3745        }
3746        if (buzz || beep || blink) {
3747            MetricsLogger.action(record.getLogMaker()
3748                    .setCategory(MetricsEvent.NOTIFICATION_ALERT)
3749                    .setType(MetricsEvent.TYPE_OPEN)
3750                    .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
3751            EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
3752        }
3753    }
3754
3755    boolean shouldMuteNotificationLocked(final NotificationRecord record) {
3756        final Notification notification = record.getNotification();
3757        if(record.isUpdate
3758                && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
3759            return true;
3760        }
3761        if (record.sbn.isGroup()) {
3762            return notification.suppressAlertingDueToGrouping();
3763        }
3764        return false;
3765    }
3766
3767    private boolean playSound(final NotificationRecord record, Uri soundUri) {
3768        boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3769        // do not play notifications if there is a user of exclusive audio focus
3770        // or the device is in vibrate mode
3771        if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
3772                != AudioManager.RINGER_MODE_VIBRATE) {
3773            final long identity = Binder.clearCallingIdentity();
3774            try {
3775                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3776                if (player != null) {
3777                    if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3778                            + " with attributes " + record.getAudioAttributes());
3779                    player.playAsync(soundUri, record.sbn.getUser(), looping,
3780                            record.getAudioAttributes());
3781                    return true;
3782                }
3783            } catch (RemoteException e) {
3784            } finally {
3785                Binder.restoreCallingIdentity(identity);
3786            }
3787        }
3788        return false;
3789    }
3790
3791    private boolean playVibration(final NotificationRecord record, long[] vibration) {
3792        // Escalate privileges so we can use the vibrator even if the
3793        // notifying app does not have the VIBRATE permission.
3794        long identity = Binder.clearCallingIdentity();
3795        try {
3796            final boolean insistent =
3797                (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3798            final VibrationEffect effect = VibrationEffect.createWaveform(
3799                    vibration, insistent ? 0 : -1 /*repeatIndex*/);
3800            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3801                    effect, record.getAudioAttributes());
3802            return true;
3803        } catch (IllegalArgumentException e) {
3804            Slog.e(TAG, "Error creating vibration waveform with pattern: " +
3805                    Arrays.toString(vibration));
3806            return false;
3807        } finally{
3808            Binder.restoreCallingIdentity(identity);
3809        }
3810    }
3811
3812    private boolean isNotificationForCurrentUser(NotificationRecord record) {
3813        final int currentUser;
3814        final long token = Binder.clearCallingIdentity();
3815        try {
3816            currentUser = ActivityManager.getCurrentUser();
3817        } finally {
3818            Binder.restoreCallingIdentity(token);
3819        }
3820        return (record.getUserId() == UserHandle.USER_ALL ||
3821                record.getUserId() == currentUser ||
3822                mUserProfiles.isCurrentProfile(record.getUserId()));
3823    }
3824
3825    private void playInCallNotification() {
3826        new Thread() {
3827            @Override
3828            public void run() {
3829                // If toneGenerator creation fails, just continue the call
3830                // without playing the notification sound.
3831                try {
3832                    synchronized (mInCallToneGeneratorLock) {
3833                        if (mInCallToneGenerator != null) {
3834                            // limit this tone to 1 second; BEEP2 should in fact be much shorter
3835                            mInCallToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2, 1000);
3836                        }
3837                    }
3838                } catch (RuntimeException e) {
3839                    Log.w(TAG, "Exception from ToneGenerator: " + e);
3840                }
3841            }
3842        }.start();
3843    }
3844
3845    void showNextToastLocked() {
3846        ToastRecord record = mToastQueue.get(0);
3847        while (record != null) {
3848            if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3849            try {
3850                record.callback.show(record.token);
3851                scheduleTimeoutLocked(record);
3852                return;
3853            } catch (RemoteException e) {
3854                Slog.w(TAG, "Object died trying to show notification " + record.callback
3855                        + " in package " + record.pkg);
3856                // remove it from the list and let the process die
3857                int index = mToastQueue.indexOf(record);
3858                if (index >= 0) {
3859                    mToastQueue.remove(index);
3860                }
3861                keepProcessAliveIfNeededLocked(record.pid);
3862                if (mToastQueue.size() > 0) {
3863                    record = mToastQueue.get(0);
3864                } else {
3865                    record = null;
3866                }
3867            }
3868        }
3869    }
3870
3871    void cancelToastLocked(int index) {
3872        ToastRecord record = mToastQueue.get(index);
3873        try {
3874            record.callback.hide();
3875        } catch (RemoteException e) {
3876            Slog.w(TAG, "Object died trying to hide notification " + record.callback
3877                    + " in package " + record.pkg);
3878            // don't worry about this, we're about to remove it from
3879            // the list anyway
3880        }
3881
3882        ToastRecord lastToast = mToastQueue.remove(index);
3883        mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
3884
3885        keepProcessAliveIfNeededLocked(record.pid);
3886        if (mToastQueue.size() > 0) {
3887            // Show the next one. If the callback fails, this will remove
3888            // it from the list, so don't assume that the list hasn't changed
3889            // after this point.
3890            showNextToastLocked();
3891        }
3892    }
3893
3894    private void scheduleTimeoutLocked(ToastRecord r)
3895    {
3896        mHandler.removeCallbacksAndMessages(r);
3897        Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3898        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3899        mHandler.sendMessageDelayed(m, delay);
3900    }
3901
3902    private void handleTimeout(ToastRecord record)
3903    {
3904        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3905        synchronized (mToastQueue) {
3906            int index = indexOfToastLocked(record.pkg, record.callback);
3907            if (index >= 0) {
3908                cancelToastLocked(index);
3909            }
3910        }
3911    }
3912
3913    // lock on mToastQueue
3914    int indexOfToastLocked(String pkg, ITransientNotification callback)
3915    {
3916        IBinder cbak = callback.asBinder();
3917        ArrayList<ToastRecord> list = mToastQueue;
3918        int len = list.size();
3919        for (int i=0; i<len; i++) {
3920            ToastRecord r = list.get(i);
3921            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3922                return i;
3923            }
3924        }
3925        return -1;
3926    }
3927
3928    // lock on mToastQueue
3929    void keepProcessAliveIfNeededLocked(int pid)
3930    {
3931        int toastCount = 0; // toasts from this pid
3932        ArrayList<ToastRecord> list = mToastQueue;
3933        int N = list.size();
3934        for (int i=0; i<N; i++) {
3935            ToastRecord r = list.get(i);
3936            if (r.pid == pid) {
3937                toastCount++;
3938            }
3939        }
3940        try {
3941            mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
3942        } catch (RemoteException e) {
3943            // Shouldn't happen.
3944        }
3945    }
3946
3947    private void handleRankingReconsideration(Message message) {
3948        if (!(message.obj instanceof RankingReconsideration)) return;
3949        RankingReconsideration recon = (RankingReconsideration) message.obj;
3950        recon.run();
3951        boolean changed;
3952        synchronized (mNotificationLock) {
3953            final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3954            if (record == null) {
3955                return;
3956            }
3957            int indexBefore = findNotificationRecordIndexLocked(record);
3958            boolean interceptBefore = record.isIntercepted();
3959            int visibilityBefore = record.getPackageVisibilityOverride();
3960            recon.applyChangesLocked(record);
3961            applyZenModeLocked(record);
3962            mRankingHelper.sort(mNotificationList);
3963            int indexAfter = findNotificationRecordIndexLocked(record);
3964            boolean interceptAfter = record.isIntercepted();
3965            int visibilityAfter = record.getPackageVisibilityOverride();
3966            changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3967                    || visibilityBefore != visibilityAfter;
3968            if (interceptBefore && !interceptAfter) {
3969                buzzBeepBlinkLocked(record);
3970            }
3971        }
3972        if (changed) {
3973            scheduleSendRankingUpdate();
3974        }
3975    }
3976
3977    private void handleRankingSort(Message msg) {
3978        if (!(msg.obj instanceof Boolean)) return;
3979        if (mRankingHelper == null) return;
3980        boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
3981        synchronized (mNotificationLock) {
3982            final int N = mNotificationList.size();
3983            // Any field that can change via one of the extractors or by the assistant
3984            // needs to be added here.
3985            ArrayList<String> orderBefore = new ArrayList<String>(N);
3986            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
3987            int[] visibilities = new int[N];
3988            boolean[] showBadges = new boolean[N];
3989            for (int i = 0; i < N; i++) {
3990                final NotificationRecord r = mNotificationList.get(i);
3991                orderBefore.add(r.getKey());
3992                groupOverrideBefore.add(r.sbn.getGroupKey());
3993                visibilities[i] = r.getPackageVisibilityOverride();
3994                showBadges[i] = r.canShowBadge();
3995                mRankingHelper.extractSignals(r);
3996            }
3997            mRankingHelper.sort(mNotificationList);
3998            for (int i = 0; i < N; i++) {
3999                final NotificationRecord r = mNotificationList.get(i);
4000                if (forceUpdate
4001                        || !orderBefore.get(i).equals(r.getKey())
4002                        || visibilities[i] != r.getPackageVisibilityOverride()
4003                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
4004                        || showBadges[i] != r.canShowBadge()) {
4005                    scheduleSendRankingUpdate();
4006                    return;
4007                }
4008            }
4009        }
4010    }
4011
4012    private void recordCallerLocked(NotificationRecord record) {
4013        if (mZenModeHelper.isCall(record)) {
4014            mZenModeHelper.recordCaller(record);
4015        }
4016    }
4017
4018    // let zen mode evaluate this record
4019    private void applyZenModeLocked(NotificationRecord record) {
4020        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4021        if (record.isIntercepted()) {
4022            int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4023                    ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4024                    | (mZenModeHelper.shouldSuppressWhenScreenOn()
4025                    ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4026            record.setSuppressedVisualEffects(suppressed);
4027        } else {
4028            record.setSuppressedVisualEffects(0);
4029        }
4030    }
4031
4032    // lock on mNotificationList
4033    private int findNotificationRecordIndexLocked(NotificationRecord target) {
4034        return mRankingHelper.indexOf(mNotificationList, target);
4035    }
4036
4037    private void scheduleSendRankingUpdate() {
4038        if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4039            Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
4040            mHandler.sendMessage(m);
4041        }
4042    }
4043
4044    private void handleSendRankingUpdate() {
4045        synchronized (mNotificationLock) {
4046            mListeners.notifyRankingUpdateLocked();
4047        }
4048    }
4049
4050    private void scheduleListenerHintsChanged(int state) {
4051        mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4052        mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4053    }
4054
4055    private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4056        mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4057        mHandler.obtainMessage(
4058                MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4059                listenerInterruptionFilter,
4060                0).sendToTarget();
4061    }
4062
4063    private void handleListenerHintsChanged(int hints) {
4064        synchronized (mNotificationLock) {
4065            mListeners.notifyListenerHintsChangedLocked(hints);
4066        }
4067    }
4068
4069    private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4070        synchronized (mNotificationLock) {
4071            mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4072        }
4073    }
4074
4075    private final class WorkerHandler extends Handler
4076    {
4077        public WorkerHandler(Looper looper) {
4078            super(looper);
4079        }
4080
4081        @Override
4082        public void handleMessage(Message msg)
4083        {
4084            switch (msg.what)
4085            {
4086                case MESSAGE_TIMEOUT:
4087                    handleTimeout((ToastRecord)msg.obj);
4088                    break;
4089                case MESSAGE_SAVE_POLICY_FILE:
4090                    handleSavePolicyFile();
4091                    break;
4092                case MESSAGE_SEND_RANKING_UPDATE:
4093                    handleSendRankingUpdate();
4094                    break;
4095                case MESSAGE_LISTENER_HINTS_CHANGED:
4096                    handleListenerHintsChanged(msg.arg1);
4097                    break;
4098                case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4099                    handleListenerInterruptionFilterChanged(msg.arg1);
4100                    break;
4101            }
4102        }
4103
4104    }
4105
4106    private final class RankingHandlerWorker extends Handler implements RankingHandler
4107    {
4108        public RankingHandlerWorker(Looper looper) {
4109            super(looper);
4110        }
4111
4112        @Override
4113        public void handleMessage(Message msg) {
4114            switch (msg.what) {
4115                case MESSAGE_RECONSIDER_RANKING:
4116                    handleRankingReconsideration(msg);
4117                    break;
4118                case MESSAGE_RANKING_SORT:
4119                    handleRankingSort(msg);
4120                    break;
4121            }
4122        }
4123
4124        public void requestSort(boolean forceUpdate) {
4125            removeMessages(MESSAGE_RANKING_SORT);
4126            Message msg = Message.obtain();
4127            msg.what = MESSAGE_RANKING_SORT;
4128            msg.obj = forceUpdate;
4129            sendMessage(msg);
4130        }
4131
4132        public void requestReconsideration(RankingReconsideration recon) {
4133            Message m = Message.obtain(this,
4134                    NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4135            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4136            sendMessageDelayed(m, delay);
4137        }
4138    }
4139
4140    // Notifications
4141    // ============================================================================
4142    static int clamp(int x, int low, int high) {
4143        return (x < low) ? low : ((x > high) ? high : x);
4144    }
4145
4146    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4147        AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
4148        if (!manager.isEnabled()) {
4149            return;
4150        }
4151
4152        AccessibilityEvent event =
4153            AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4154        event.setPackageName(packageName);
4155        event.setClassName(Notification.class.getName());
4156        event.setParcelableData(notification);
4157        CharSequence tickerText = notification.tickerText;
4158        if (!TextUtils.isEmpty(tickerText)) {
4159            event.getText().add(tickerText);
4160        }
4161
4162        manager.sendAccessibilityEvent(event);
4163    }
4164
4165    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
4166        final String canceledKey = r.getKey();
4167
4168        // Remove from both lists, either list could have a separate Record for what is effectively
4169        // the same notification.
4170        boolean wasPosted = false;
4171        NotificationRecord recordInList = null;
4172        if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey())) != null) {
4173            mNotificationList.remove(recordInList);
4174            mNotificationsByKey.remove(recordInList.sbn.getKey());
4175            wasPosted = true;
4176        }
4177        while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4178                != null) {
4179            mEnqueuedNotifications.remove(recordInList);
4180        }
4181
4182        // Record caller.
4183        recordCallerLocked(r);
4184
4185        // tell the app
4186        if (sendDelete) {
4187            if (r.getNotification().deleteIntent != null) {
4188                try {
4189                    r.getNotification().deleteIntent.send();
4190                } catch (PendingIntent.CanceledException ex) {
4191                    // do nothing - there's no relevant way to recover, and
4192                    //     no reason to let this propagate
4193                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4194                }
4195            }
4196        }
4197
4198        // Only cancel these if this notification actually got to be posted.
4199        if (wasPosted) {
4200            // status bar
4201            if (r.getNotification().getSmallIcon() != null) {
4202                if (reason != REASON_SNOOZED) {
4203                    r.isCanceled = true;
4204                }
4205                mListeners.notifyRemovedLocked(r.sbn, reason);
4206                mHandler.post(new Runnable() {
4207                    @Override
4208                    public void run() {
4209                        mGroupHelper.onNotificationRemoved(r.sbn);
4210                    }
4211                });
4212            }
4213
4214            // sound
4215            if (canceledKey.equals(mSoundNotificationKey)) {
4216                mSoundNotificationKey = null;
4217                final long identity = Binder.clearCallingIdentity();
4218                try {
4219                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4220                    if (player != null) {
4221                        player.stopAsync();
4222                    }
4223                } catch (RemoteException e) {
4224                } finally {
4225                    Binder.restoreCallingIdentity(identity);
4226                }
4227            }
4228
4229            // vibrate
4230            if (canceledKey.equals(mVibrateNotificationKey)) {
4231                mVibrateNotificationKey = null;
4232                long identity = Binder.clearCallingIdentity();
4233                try {
4234                    mVibrator.cancel();
4235                }
4236                finally {
4237                    Binder.restoreCallingIdentity(identity);
4238                }
4239            }
4240
4241            // light
4242            mLights.remove(canceledKey);
4243        }
4244
4245        // Record usage stats
4246        // TODO: add unbundling stats?
4247        switch (reason) {
4248            case REASON_CANCEL:
4249            case REASON_CANCEL_ALL:
4250            case REASON_LISTENER_CANCEL:
4251            case REASON_LISTENER_CANCEL_ALL:
4252                mUsageStats.registerDismissedByUser(r);
4253                break;
4254            case REASON_APP_CANCEL:
4255            case REASON_APP_CANCEL_ALL:
4256                mUsageStats.registerRemovedByApp(r);
4257                break;
4258        }
4259
4260        String groupKey = r.getGroupKey();
4261        NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4262        if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4263            mSummaryByGroupKey.remove(groupKey);
4264        }
4265        final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4266        if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4267            summaries.remove(r.sbn.getPackageName());
4268        }
4269
4270        // Save it for users of getHistoricalNotifications()
4271        mArchive.record(r.sbn);
4272
4273        final long now = System.currentTimeMillis();
4274        MetricsLogger.action(r.getLogMaker(now)
4275                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4276                .setType(MetricsEvent.TYPE_DISMISS)
4277                .setSubtype(reason));
4278        EventLogTags.writeNotificationCanceled(canceledKey, reason,
4279                r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
4280    }
4281
4282    /**
4283     * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4284     * and none of the {@code mustNotHaveFlags}.
4285     */
4286    void cancelNotification(final int callingUid, final int callingPid,
4287            final String pkg, final String tag, final int id,
4288            final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4289            final int userId, final int reason, final ManagedServiceInfo listener) {
4290        // In enqueueNotificationInternal notifications are added by scheduling the
4291        // work on the worker handler. Hence, we also schedule the cancel on this
4292        // handler to avoid a scenario where an add notification call followed by a
4293        // remove notification call ends up in not removing the notification.
4294        mHandler.post(new Runnable() {
4295            @Override
4296            public void run() {
4297                String listenerName = listener == null ? null : listener.component.toShortString();
4298                if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4299                        userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4300
4301                synchronized (mNotificationLock) {
4302                    // Look for the notification, searching both the posted and enqueued lists.
4303                    NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4304                    if (r != null) {
4305                        // The notification was found, check if it should be removed.
4306
4307                        // Ideally we'd do this in the caller of this method. However, that would
4308                        // require the caller to also find the notification.
4309                        if (reason == REASON_CLICK) {
4310                            mUsageStats.registerClickedByUser(r);
4311                        }
4312
4313                        if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4314                            return;
4315                        }
4316                        if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4317                            return;
4318                        }
4319
4320                        // Cancel the notification.
4321                        cancelNotificationLocked(r, sendDelete, reason);
4322                        cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4323                                sendDelete);
4324                        updateLightsLocked();
4325                    } else {
4326                        // No notification was found, assume that it is snoozed and cancel it.
4327                        if (reason != REASON_SNOOZED) {
4328                            final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4329                            if (wasSnoozed) {
4330                                savePolicyFile();
4331                            }
4332                        }
4333                    }
4334                }
4335            }
4336        });
4337    }
4338
4339    /**
4340     * Determine whether the userId applies to the notification in question, either because
4341     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4342     */
4343    private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4344        return
4345                // looking for USER_ALL notifications? match everything
4346                   userId == UserHandle.USER_ALL
4347                // a notification sent to USER_ALL matches any query
4348                || r.getUserId() == UserHandle.USER_ALL
4349                // an exact user match
4350                || r.getUserId() == userId;
4351    }
4352
4353    /**
4354     * Determine whether the userId applies to the notification in question, either because
4355     * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4356     * because it matches one of the users profiles.
4357     */
4358    private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4359        return notificationMatchesUserId(r, userId)
4360                || mUserProfiles.isCurrentProfile(r.getUserId());
4361    }
4362
4363    /**
4364     * Cancels all notifications from a given package that have all of the
4365     * {@code mustHaveFlags}.
4366     */
4367    void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4368            int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4369            ManagedServiceInfo listener) {
4370        mHandler.post(new Runnable() {
4371            @Override
4372            public void run() {
4373                String listenerName = listener == null ? null : listener.component.toShortString();
4374                EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4375                        pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4376                        listenerName);
4377
4378                // Why does this parameter exist? Do we actually want to execute the above if doit
4379                // is false?
4380                if (!doit) {
4381                    return;
4382                }
4383
4384                synchronized (mNotificationLock) {
4385                    FlagChecker flagChecker = (int flags) -> {
4386                        if ((flags & mustHaveFlags) != mustHaveFlags) {
4387                            return false;
4388                        }
4389                        if ((flags & mustNotHaveFlags) != 0) {
4390                            return false;
4391                        }
4392                        return true;
4393                    };
4394
4395                    cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4396                            pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4397                            false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4398                            listenerName);
4399                    cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4400                            callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4401                            flagChecker, false /*includeCurrentProfiles*/, userId,
4402                            false /*sendDelete*/, reason, listenerName);
4403                    mSnoozeHelper.cancel(userId, pkg);
4404                }
4405            }
4406        });
4407    }
4408
4409    private interface FlagChecker {
4410        // Returns false if these flags do not pass the defined flag test.
4411        public boolean apply(int flags);
4412    }
4413
4414    private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4415            int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4416            String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4417            boolean sendDelete, int reason, String listenerName) {
4418        ArrayList<NotificationRecord> canceledNotifications = null;
4419        for (int i = notificationList.size() - 1; i >= 0; --i) {
4420            NotificationRecord r = notificationList.get(i);
4421            if (includeCurrentProfiles) {
4422                if (!notificationMatchesCurrentProfiles(r, userId)) {
4423                    continue;
4424                }
4425            } else if (!notificationMatchesUserId(r, userId)) {
4426                continue;
4427            }
4428            // Don't remove notifications to all, if there's no package name specified
4429            if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4430                continue;
4431            }
4432            if (!flagChecker.apply(r.getFlags())) {
4433                continue;
4434            }
4435            if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4436                continue;
4437            }
4438            if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4439                continue;
4440            }
4441
4442            if (canceledNotifications == null) {
4443                canceledNotifications = new ArrayList<>();
4444            }
4445            canceledNotifications.add(r);
4446            cancelNotificationLocked(r, sendDelete, reason);
4447        }
4448        if (canceledNotifications != null) {
4449            final int M = canceledNotifications.size();
4450            for (int i = 0; i < M; i++) {
4451                cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4452                        listenerName, false /* sendDelete */);
4453            }
4454            updateLightsLocked();
4455        }
4456    }
4457
4458    void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4459            ManagedServiceInfo listener) {
4460        String listenerName = listener == null ? null : listener.component.toShortString();
4461        if (duration <= 0 && snoozeCriterionId == null || key == null) {
4462            return;
4463        }
4464
4465        if (DBG) {
4466            Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4467                    snoozeCriterionId, listenerName));
4468        }
4469        // Needs to post so that it can cancel notifications not yet enqueued.
4470        mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4471    }
4472
4473    void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4474        String listenerName = listener == null ? null : listener.component.toShortString();
4475        if (DBG) {
4476            Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4477        }
4478        mSnoozeHelper.repost(key);
4479        savePolicyFile();
4480    }
4481
4482    void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4483            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4484        mHandler.post(new Runnable() {
4485            @Override
4486            public void run() {
4487                synchronized (mNotificationLock) {
4488                    String listenerName =
4489                            listener == null ? null : listener.component.toShortString();
4490                    EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4491                            null, userId, 0, 0, reason, listenerName);
4492
4493                    FlagChecker flagChecker = (int flags) -> {
4494                        if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4495                                != 0) {
4496                            return false;
4497                        }
4498                        return true;
4499                    };
4500
4501                    cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4502                            null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4503                            includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4504                            listenerName);
4505                    cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4506                            callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4507                            flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4508                            reason, listenerName);
4509                    mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4510                }
4511            }
4512        });
4513    }
4514
4515    // Warning: The caller is responsible for invoking updateLightsLocked().
4516    private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4517            String listenerName, boolean sendDelete) {
4518        Notification n = r.getNotification();
4519        if (!n.isGroupSummary()) {
4520            return;
4521        }
4522
4523        String pkg = r.sbn.getPackageName();
4524        int userId = r.getUserId();
4525
4526        if (pkg == null) {
4527            if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4528            return;
4529        }
4530
4531        cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4532                sendDelete);
4533        cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4534                listenerName, sendDelete);
4535    }
4536
4537    private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4538            NotificationRecord parentNotification, int callingUid, int callingPid,
4539            String listenerName, boolean sendDelete) {
4540        final String pkg = parentNotification.sbn.getPackageName();
4541        final int userId = parentNotification.getUserId();
4542        final int reason = REASON_GROUP_SUMMARY_CANCELED;
4543        for (int i = notificationList.size() - 1; i >= 0; i--) {
4544            final NotificationRecord childR = notificationList.get(i);
4545            final StatusBarNotification childSbn = childR.sbn;
4546            if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4547                    childR.getGroupKey().equals(parentNotification.getGroupKey())
4548                    && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
4549                EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4550                        childSbn.getTag(), userId, 0, 0, reason, listenerName);
4551                cancelNotificationLocked(childR, sendDelete, reason);
4552            }
4553        }
4554    }
4555
4556    // lock on mNotificationList
4557    void updateLightsLocked()
4558    {
4559        // handle notification lights
4560        NotificationRecord ledNotification = null;
4561        while (ledNotification == null && !mLights.isEmpty()) {
4562            final String owner = mLights.get(mLights.size() - 1);
4563            ledNotification = mNotificationsByKey.get(owner);
4564            if (ledNotification == null) {
4565                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4566                mLights.remove(owner);
4567            }
4568        }
4569
4570        // Don't flash while we are in a call or screen is on
4571        if (ledNotification == null || mInCall || mScreenOn) {
4572            mNotificationLight.turnOff();
4573        } else {
4574            NotificationRecord.Light light = ledNotification.getLight();
4575            if (light != null && mNotificationPulseEnabled) {
4576                // pulse repeatedly
4577                mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4578                        light.onMs, light.offMs);
4579            }
4580        }
4581    }
4582
4583    @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4584            String groupKey, int userId) {
4585        List<NotificationRecord> records = new ArrayList<>();
4586        records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4587        records.addAll(
4588                findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4589        return records;
4590    }
4591
4592
4593    private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4594            ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4595        List<NotificationRecord> records = new ArrayList<>();
4596        final int len = list.size();
4597        for (int i = 0; i < len; i++) {
4598            NotificationRecord r = list.get(i);
4599            if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4600                    && r.sbn.getPackageName().equals(pkg)) {
4601                records.add(r);
4602            }
4603        }
4604        return records;
4605    }
4606
4607    // Searches both enqueued and posted notifications by key.
4608    // TODO: need to combine a bunch of these getters with slightly different behavior.
4609    // TODO: Should enqueuing just add to mNotificationsByKey instead?
4610    private NotificationRecord findNotificationByKeyLocked(String key) {
4611        NotificationRecord r;
4612        if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4613            return r;
4614        }
4615        if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4616            return r;
4617        }
4618        return null;
4619    }
4620
4621    NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
4622        NotificationRecord r;
4623        if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4624            return r;
4625        }
4626        if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4627                != null) {
4628            return r;
4629        }
4630        return null;
4631    }
4632
4633    private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4634            String pkg, String tag, int id, int userId) {
4635        final int len = list.size();
4636        for (int i = 0; i < len; i++) {
4637            NotificationRecord r = list.get(i);
4638            if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
4639                    TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
4640                return r;
4641            }
4642        }
4643        return null;
4644    }
4645
4646    private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4647            String key)
4648    {
4649        final int N = list.size();
4650        for (int i = 0; i < N; i++) {
4651            if (key.equals(list.get(i).getKey())) {
4652                return list.get(i);
4653            }
4654        }
4655        return null;
4656    }
4657
4658    // lock on mNotificationList
4659    int indexOfNotificationLocked(String key) {
4660        final int N = mNotificationList.size();
4661        for (int i = 0; i < N; i++) {
4662            if (key.equals(mNotificationList.get(i).getKey())) {
4663                return i;
4664            }
4665        }
4666        return -1;
4667    }
4668
4669    private void updateNotificationPulse() {
4670        synchronized (mNotificationLock) {
4671            updateLightsLocked();
4672        }
4673    }
4674
4675    protected boolean isCallingUidSystem() {
4676        final int uid = Binder.getCallingUid();
4677        return uid == Process.SYSTEM_UID;
4678    }
4679
4680    protected boolean isUidSystemOrPhone(int uid) {
4681        final int appid = UserHandle.getAppId(uid);
4682        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
4683    }
4684
4685    // TODO: Most calls should probably move to isCallerSystem.
4686    protected boolean isCallerSystemOrPhone() {
4687        return isUidSystemOrPhone(Binder.getCallingUid());
4688    }
4689
4690    private void checkCallerIsSystem() {
4691        if (isCallerSystemOrPhone()) {
4692            return;
4693        }
4694        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
4695    }
4696
4697    private void checkCallerIsSystemOrSameApp(String pkg) {
4698        if (isCallerSystemOrPhone()) {
4699            return;
4700        }
4701        checkCallerIsSameApp(pkg);
4702    }
4703
4704    private boolean isCallerInstantApp(String pkg) {
4705        // System is always allowed to act for ephemeral apps.
4706        if (isCallerSystemOrPhone()) {
4707            return false;
4708        }
4709
4710        mAppOps.checkPackage(Binder.getCallingUid(), pkg);
4711
4712        try {
4713            ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
4714                    UserHandle.getCallingUserId());
4715            if (ai == null) {
4716                throw new SecurityException("Unknown package " + pkg);
4717            }
4718            return ai.isInstantApp();
4719        } catch (RemoteException re) {
4720            throw new SecurityException("Unknown package " + pkg, re);
4721        }
4722
4723    }
4724
4725    private void checkCallerIsSameApp(String pkg) {
4726        final int uid = Binder.getCallingUid();
4727        try {
4728            ApplicationInfo ai = mPackageManager.getApplicationInfo(
4729                    pkg, 0, UserHandle.getCallingUserId());
4730            if (ai == null) {
4731                throw new SecurityException("Unknown package " + pkg);
4732            }
4733            if (!UserHandle.isSameApp(ai.uid, uid)) {
4734                throw new SecurityException("Calling uid " + uid + " gave package "
4735                        + pkg + " which is owned by uid " + ai.uid);
4736            }
4737        } catch (RemoteException re) {
4738            throw new SecurityException("Unknown package " + pkg + "\n" + re);
4739        }
4740    }
4741
4742    private static String callStateToString(int state) {
4743        switch (state) {
4744            case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
4745            case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
4746            case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
4747            default: return "CALL_STATE_UNKNOWN_" + state;
4748        }
4749    }
4750
4751    private void listenForCallState() {
4752        TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
4753            @Override
4754            public void onCallStateChanged(int state, String incomingNumber) {
4755                if (mCallState == state) return;
4756                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
4757                mCallState = state;
4758            }
4759        }, PhoneStateListener.LISTEN_CALL_STATE);
4760    }
4761
4762    /**
4763     * Generates a NotificationRankingUpdate from 'sbns', considering only
4764     * notifications visible to the given listener.
4765     *
4766     * <p>Caller must hold a lock on mNotificationList.</p>
4767     */
4768    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
4769        final int N = mNotificationList.size();
4770        ArrayList<String> keys = new ArrayList<String>(N);
4771        ArrayList<String> interceptedKeys = new ArrayList<String>(N);
4772        ArrayList<Integer> importance = new ArrayList<>(N);
4773        Bundle overrideGroupKeys = new Bundle();
4774        Bundle visibilityOverrides = new Bundle();
4775        Bundle suppressedVisualEffects = new Bundle();
4776        Bundle explanation = new Bundle();
4777        Bundle channels = new Bundle();
4778        Bundle overridePeople = new Bundle();
4779        Bundle snoozeCriteria = new Bundle();
4780        Bundle showBadge = new Bundle();
4781        for (int i = 0; i < N; i++) {
4782            NotificationRecord record = mNotificationList.get(i);
4783            if (!isVisibleToListener(record.sbn, info)) {
4784                continue;
4785            }
4786            final String key = record.sbn.getKey();
4787            keys.add(key);
4788            importance.add(record.getImportance());
4789            if (record.getImportanceExplanation() != null) {
4790                explanation.putCharSequence(key, record.getImportanceExplanation());
4791            }
4792            if (record.isIntercepted()) {
4793                interceptedKeys.add(key);
4794
4795            }
4796            suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
4797            if (record.getPackageVisibilityOverride()
4798                    != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
4799                visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
4800            }
4801            overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
4802            channels.putParcelable(key, record.getChannel());
4803            overridePeople.putStringArrayList(key, record.getPeopleOverride());
4804            snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
4805            showBadge.putBoolean(key, record.canShowBadge());
4806        }
4807        final int M = keys.size();
4808        String[] keysAr = keys.toArray(new String[M]);
4809        String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
4810        int[] importanceAr = new int[M];
4811        for (int i = 0; i < M; i++) {
4812            importanceAr[i] = importance.get(i);
4813        }
4814        return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
4815                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
4816                channels, overridePeople, snoozeCriteria, showBadge);
4817    }
4818
4819    boolean hasCompanionDevice(ManagedServiceInfo info) {
4820        if (mCompanionManager == null) {
4821            mCompanionManager = getCompanionManager();
4822        }
4823        // Companion mgr doesn't exist on all device types
4824        if (mCompanionManager == null) {
4825            return false;
4826        }
4827        long identity = Binder.clearCallingIdentity();
4828        try {
4829            List<String> associations = mCompanionManager.getAssociations(
4830                    info.component.getPackageName(), info.userid);
4831            if (!ArrayUtils.isEmpty(associations)) {
4832                return true;
4833            }
4834        } catch (SecurityException se) {
4835            // Not a privileged listener
4836        } catch (RemoteException re) {
4837            Slog.e(TAG, "Cannot reach companion device service", re);
4838        } catch (Exception e) {
4839            Slog.e(TAG, "Cannot verify listener " + info, e);
4840        } finally {
4841            Binder.restoreCallingIdentity(identity);
4842        }
4843        return false;
4844    }
4845
4846    protected ICompanionDeviceManager getCompanionManager() {
4847        return ICompanionDeviceManager.Stub.asInterface(
4848                ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
4849    }
4850
4851    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
4852        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
4853            return false;
4854        }
4855        // TODO: remove this for older listeners.
4856        return true;
4857    }
4858
4859    private boolean isPackageSuspendedForUser(String pkg, int uid) {
4860        int userId = UserHandle.getUserId(uid);
4861        try {
4862            return mPackageManager.isPackageSuspendedForUser(pkg, userId);
4863        } catch (RemoteException re) {
4864            throw new SecurityException("Could not talk to package manager service");
4865        } catch (IllegalArgumentException ex) {
4866            // Package not found.
4867            return false;
4868        }
4869    }
4870
4871    private class TrimCache {
4872        StatusBarNotification heavy;
4873        StatusBarNotification sbnClone;
4874        StatusBarNotification sbnCloneLight;
4875
4876        TrimCache(StatusBarNotification sbn) {
4877            heavy = sbn;
4878        }
4879
4880        StatusBarNotification ForListener(ManagedServiceInfo info) {
4881            if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
4882                if (sbnCloneLight == null) {
4883                    sbnCloneLight = heavy.cloneLight();
4884                }
4885                return sbnCloneLight;
4886            } else {
4887                if (sbnClone == null) {
4888                    sbnClone = heavy.clone();
4889                }
4890                return sbnClone;
4891            }
4892        }
4893    }
4894
4895    public class NotificationAssistants extends ManagedServices {
4896
4897        public NotificationAssistants() {
4898            super(getContext(), mHandler, mNotificationList, mUserProfiles);
4899        }
4900
4901        @Override
4902        protected Config getConfig() {
4903            Config c = new Config();
4904            c.caption = "notification assistant service";
4905            c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
4906            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
4907            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
4908            c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
4909            c.clientLabel = R.string.notification_ranker_binding_label;
4910            return c;
4911        }
4912
4913        @Override
4914        protected IInterface asInterface(IBinder binder) {
4915            return INotificationListener.Stub.asInterface(binder);
4916        }
4917
4918        @Override
4919        protected boolean checkType(IInterface service) {
4920            return service instanceof INotificationListener;
4921        }
4922
4923        @Override
4924        protected void onServiceAdded(ManagedServiceInfo info) {
4925            mListeners.registerGuestService(info);
4926        }
4927
4928        @Override
4929        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
4930            mListeners.unregisterService(removed.service, removed.userid);
4931        }
4932
4933        public void onNotificationEnqueued(final NotificationRecord r) {
4934            final StatusBarNotification sbn = r.sbn;
4935            TrimCache trimCache = new TrimCache(sbn);
4936
4937            // There should be only one, but it's a list, so while we enforce
4938            // singularity elsewhere, we keep it general here, to avoid surprises.
4939            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
4940                boolean sbnVisible = isVisibleToListener(sbn, info);
4941                if (!sbnVisible) {
4942                    continue;
4943                }
4944
4945                final int importance = r.getImportance();
4946                final boolean fromUser = r.isImportanceFromUser();
4947                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
4948                mHandler.post(new Runnable() {
4949                    @Override
4950                    public void run() {
4951                        notifyEnqueued(info, sbnToPost);
4952                    }
4953                });
4954            }
4955        }
4956
4957        private void notifyEnqueued(final ManagedServiceInfo info,
4958                final StatusBarNotification sbn) {
4959            final INotificationListener assistant = (INotificationListener) info.service;
4960            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4961            try {
4962                assistant.onNotificationEnqueued(sbnHolder);
4963            } catch (RemoteException ex) {
4964                Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
4965            }
4966        }
4967
4968        /**
4969         * asynchronously notify the assistant that a notification has been snoozed until a
4970         * context
4971         */
4972        public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
4973                final String snoozeCriterionId) {
4974            TrimCache trimCache = new TrimCache(sbn);
4975            for (final ManagedServiceInfo info : getServices()) {
4976                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
4977                mHandler.post(new Runnable() {
4978                    @Override
4979                    public void run() {
4980                        final INotificationListener assistant =
4981                                (INotificationListener) info.service;
4982                        StatusBarNotificationHolder sbnHolder
4983                                = new StatusBarNotificationHolder(sbnToPost);
4984                        try {
4985                            assistant.onNotificationSnoozedUntilContext(
4986                                    sbnHolder, snoozeCriterionId);
4987                        } catch (RemoteException ex) {
4988                            Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
4989                        }
4990                    }
4991                });
4992            }
4993        }
4994
4995        public boolean isEnabled() {
4996            return !getServices().isEmpty();
4997        }
4998    }
4999
5000    public class NotificationListeners extends ManagedServices {
5001
5002        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5003
5004        public NotificationListeners() {
5005            super(getContext(), mHandler, mNotificationList, mUserProfiles);
5006        }
5007
5008        @Override
5009        protected Config getConfig() {
5010            Config c = new Config();
5011            c.caption = "notification listener";
5012            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5013            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5014            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5015            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5016            c.clientLabel = R.string.notification_listener_binding_label;
5017            return c;
5018        }
5019
5020        @Override
5021        protected IInterface asInterface(IBinder binder) {
5022            return INotificationListener.Stub.asInterface(binder);
5023        }
5024
5025        @Override
5026        protected boolean checkType(IInterface service) {
5027            return service instanceof INotificationListener;
5028        }
5029
5030        @Override
5031        public void onServiceAdded(ManagedServiceInfo info) {
5032            final INotificationListener listener = (INotificationListener) info.service;
5033            final NotificationRankingUpdate update;
5034            synchronized (mNotificationLock) {
5035                update = makeRankingUpdateLocked(info);
5036            }
5037            try {
5038                listener.onListenerConnected(update);
5039            } catch (RemoteException e) {
5040                // we tried
5041            }
5042        }
5043
5044        @Override
5045        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5046            if (removeDisabledHints(removed)) {
5047                updateListenerHintsLocked();
5048                updateEffectsSuppressorLocked();
5049            }
5050            mLightTrimListeners.remove(removed);
5051        }
5052
5053        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5054            if (trim == TRIM_LIGHT) {
5055                mLightTrimListeners.add(info);
5056            } else {
5057                mLightTrimListeners.remove(info);
5058            }
5059        }
5060
5061        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5062            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5063        }
5064
5065        /**
5066         * asynchronously notify all listeners about a new notification
5067         *
5068         * <p>
5069         * Also takes care of removing a notification that has been visible to a listener before,
5070         * but isn't anymore.
5071         */
5072        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5073            // Lazily initialized snapshots of the notification.
5074            TrimCache trimCache = new TrimCache(sbn);
5075
5076            for (final ManagedServiceInfo info : getServices()) {
5077                boolean sbnVisible = isVisibleToListener(sbn, info);
5078                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5079                // This notification hasn't been and still isn't visible -> ignore.
5080                if (!oldSbnVisible && !sbnVisible) {
5081                    continue;
5082                }
5083                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5084
5085                // This notification became invisible -> remove the old one.
5086                if (oldSbnVisible && !sbnVisible) {
5087                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5088                    mHandler.post(new Runnable() {
5089                        @Override
5090                        public void run() {
5091                            notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5092                        }
5093                    });
5094                    continue;
5095                }
5096
5097                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
5098                mHandler.post(new Runnable() {
5099                    @Override
5100                    public void run() {
5101                        notifyPosted(info, sbnToPost, update);
5102                    }
5103                });
5104            }
5105        }
5106
5107        /**
5108         * asynchronously notify all listeners about a removed notification
5109         */
5110        public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5111            // make a copy in case changes are made to the underlying Notification object
5112            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5113            // notification
5114            final StatusBarNotification sbnLight = sbn.cloneLight();
5115            for (final ManagedServiceInfo info : getServices()) {
5116                if (!isVisibleToListener(sbn, info)) {
5117                    continue;
5118                }
5119                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5120                mHandler.post(new Runnable() {
5121                    @Override
5122                    public void run() {
5123                        notifyRemoved(info, sbnLight, update, reason);
5124                    }
5125                });
5126            }
5127        }
5128
5129        /**
5130         * asynchronously notify all listeners about a reordering of notifications
5131         */
5132        public void notifyRankingUpdateLocked() {
5133            for (final ManagedServiceInfo serviceInfo : getServices()) {
5134                if (!serviceInfo.isEnabledForCurrentProfiles()) {
5135                    continue;
5136                }
5137                final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5138                mHandler.post(new Runnable() {
5139                    @Override
5140                    public void run() {
5141                        notifyRankingUpdate(serviceInfo, update);
5142                    }
5143                });
5144            }
5145        }
5146
5147        public void notifyListenerHintsChangedLocked(final int hints) {
5148            for (final ManagedServiceInfo serviceInfo : getServices()) {
5149                if (!serviceInfo.isEnabledForCurrentProfiles()) {
5150                    continue;
5151                }
5152                mHandler.post(new Runnable() {
5153                    @Override
5154                    public void run() {
5155                        notifyListenerHintsChanged(serviceInfo, hints);
5156                    }
5157                });
5158            }
5159        }
5160
5161        public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5162            for (final ManagedServiceInfo serviceInfo : getServices()) {
5163                if (!serviceInfo.isEnabledForCurrentProfiles()) {
5164                    continue;
5165                }
5166                mHandler.post(new Runnable() {
5167                    @Override
5168                    public void run() {
5169                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5170                    }
5171                });
5172            }
5173        }
5174
5175        protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5176                final NotificationChannel channel, final int modificationType) {
5177            if (channel == null) {
5178                return;
5179            }
5180            for (final ManagedServiceInfo serviceInfo : getServices()) {
5181                if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5182                    continue;
5183                }
5184
5185                mHandler.post(new Runnable() {
5186                    @Override
5187                    public void run() {
5188                        if (hasCompanionDevice(serviceInfo)) {
5189                            notifyNotificationChannelChanged(
5190                                    serviceInfo, pkg, user, channel, modificationType);
5191                        }
5192                    }
5193                });
5194            }
5195        }
5196
5197        protected void notifyNotificationChannelGroupChanged(
5198                final String pkg, final UserHandle user, final NotificationChannelGroup group,
5199                final int modificationType) {
5200            if (group == null) {
5201                return;
5202            }
5203            for (final ManagedServiceInfo serviceInfo : getServices()) {
5204                if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5205                    continue;
5206                }
5207
5208                mHandler.post(new Runnable() {
5209                    @Override
5210                    public void run() {
5211                        if (hasCompanionDevice(serviceInfo)) {
5212                            notifyNotificationChannelGroupChanged(
5213                                    serviceInfo, pkg, user, group, modificationType);
5214                        }
5215                    }
5216                });
5217            }
5218        }
5219
5220        private void notifyPosted(final ManagedServiceInfo info,
5221                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5222            final INotificationListener listener = (INotificationListener) info.service;
5223            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5224            try {
5225                listener.onNotificationPosted(sbnHolder, rankingUpdate);
5226            } catch (RemoteException ex) {
5227                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5228            }
5229        }
5230
5231        private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5232                NotificationRankingUpdate rankingUpdate, int reason) {
5233            if (!info.enabledAndUserMatches(sbn.getUserId())) {
5234                return;
5235            }
5236            final INotificationListener listener = (INotificationListener) info.service;
5237            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5238            try {
5239                listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5240            } catch (RemoteException ex) {
5241                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5242            }
5243        }
5244
5245        private void notifyRankingUpdate(ManagedServiceInfo info,
5246                                         NotificationRankingUpdate rankingUpdate) {
5247            final INotificationListener listener = (INotificationListener) info.service;
5248            try {
5249                listener.onNotificationRankingUpdate(rankingUpdate);
5250            } catch (RemoteException ex) {
5251                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5252            }
5253        }
5254
5255        private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5256            final INotificationListener listener = (INotificationListener) info.service;
5257            try {
5258                listener.onListenerHintsChanged(hints);
5259            } catch (RemoteException ex) {
5260                Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5261            }
5262        }
5263
5264        private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5265                int interruptionFilter) {
5266            final INotificationListener listener = (INotificationListener) info.service;
5267            try {
5268                listener.onInterruptionFilterChanged(interruptionFilter);
5269            } catch (RemoteException ex) {
5270                Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5271            }
5272        }
5273
5274        void notifyNotificationChannelChanged(ManagedServiceInfo info,
5275                final String pkg, final UserHandle user, final NotificationChannel channel,
5276                final int modificationType) {
5277            final INotificationListener listener = (INotificationListener) info.service;
5278            try {
5279                listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5280            } catch (RemoteException ex) {
5281                Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5282            }
5283        }
5284
5285        private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5286                final String pkg, final UserHandle user, final NotificationChannelGroup group,
5287                final int modificationType) {
5288            final INotificationListener listener = (INotificationListener) info.service;
5289            try {
5290                listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5291            } catch (RemoteException ex) {
5292                Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5293            }
5294        }
5295
5296        public boolean isListenerPackage(String packageName) {
5297            if (packageName == null) {
5298                return false;
5299            }
5300            // TODO: clean up locking object later
5301            synchronized (mNotificationLock) {
5302                for (final ManagedServiceInfo serviceInfo : getServices()) {
5303                    if (packageName.equals(serviceInfo.component.getPackageName())) {
5304                        return true;
5305                    }
5306                }
5307            }
5308            return false;
5309        }
5310    }
5311
5312    public static final class DumpFilter {
5313        public boolean filtered = false;
5314        public String pkgFilter;
5315        public boolean zen;
5316        public long since;
5317        public boolean stats;
5318        public boolean redact = true;
5319        public boolean proto = false;
5320
5321        public static DumpFilter parseFromArguments(String[] args) {
5322            final DumpFilter filter = new DumpFilter();
5323            for (int ai = 0; ai < args.length; ai++) {
5324                final String a = args[ai];
5325                if ("--proto".equals(args[0])) {
5326                    filter.proto = true;
5327                }
5328                if ("--noredact".equals(a) || "--reveal".equals(a)) {
5329                    filter.redact = false;
5330                } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5331                    if (ai < args.length-1) {
5332                        ai++;
5333                        filter.pkgFilter = args[ai].trim().toLowerCase();
5334                        if (filter.pkgFilter.isEmpty()) {
5335                            filter.pkgFilter = null;
5336                        } else {
5337                            filter.filtered = true;
5338                        }
5339                    }
5340                } else if ("--zen".equals(a) || "zen".equals(a)) {
5341                    filter.filtered = true;
5342                    filter.zen = true;
5343                } else if ("--stats".equals(a)) {
5344                    filter.stats = true;
5345                    if (ai < args.length-1) {
5346                        ai++;
5347                        filter.since = Long.parseLong(args[ai]);
5348                    } else {
5349                        filter.since = 0;
5350                    }
5351                }
5352            }
5353            return filter;
5354        }
5355
5356        public boolean matches(StatusBarNotification sbn) {
5357            if (!filtered) return true;
5358            return zen ? true : sbn != null
5359                    && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5360        }
5361
5362        public boolean matches(ComponentName component) {
5363            if (!filtered) return true;
5364            return zen ? true : component != null && matches(component.getPackageName());
5365        }
5366
5367        public boolean matches(String pkg) {
5368            if (!filtered) return true;
5369            return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5370        }
5371
5372        @Override
5373        public String toString() {
5374            return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5375        }
5376    }
5377
5378    /**
5379     * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5380     * binder without sending large amounts of data over a oneway transaction.
5381     */
5382    private static final class StatusBarNotificationHolder
5383            extends IStatusBarNotificationHolder.Stub {
5384        private StatusBarNotification mValue;
5385
5386        public StatusBarNotificationHolder(StatusBarNotification value) {
5387            mValue = value;
5388        }
5389
5390        /** Get the held value and clear it. This function should only be called once per holder */
5391        @Override
5392        public StatusBarNotification get() {
5393            StatusBarNotification value = mValue;
5394            mValue = null;
5395            return value;
5396        }
5397    }
5398
5399    private final class PolicyAccess {
5400        private static final String SEPARATOR = ":";
5401        private final String[] PERM = {
5402            android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
5403        };
5404
5405        public boolean isPackageGranted(String pkg) {
5406            return pkg != null && getGrantedPackages().contains(pkg);
5407        }
5408
5409        public void put(String pkg, boolean granted) {
5410            if (pkg == null) return;
5411            final ArraySet<String> pkgs = getGrantedPackages();
5412            boolean changed;
5413            if (granted) {
5414                changed = pkgs.add(pkg);
5415            } else {
5416                changed = pkgs.remove(pkg);
5417            }
5418            if (!changed) return;
5419            final String setting = TextUtils.join(SEPARATOR, pkgs);
5420            final int currentUser = ActivityManager.getCurrentUser();
5421            Settings.Secure.putStringForUser(getContext().getContentResolver(),
5422                    Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5423                    setting,
5424                    currentUser);
5425            getContext().sendBroadcastAsUser(new Intent(NotificationManager
5426                    .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
5427                .setPackage(pkg)
5428                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
5429        }
5430
5431        public ArraySet<String> getGrantedPackages() {
5432            final ArraySet<String> pkgs = new ArraySet<>();
5433
5434            long identity = Binder.clearCallingIdentity();
5435            try {
5436                final String setting = Settings.Secure.getStringForUser(
5437                        getContext().getContentResolver(),
5438                        Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5439                        ActivityManager.getCurrentUser());
5440                if (setting != null) {
5441                    final String[] tokens = setting.split(SEPARATOR);
5442                    for (int i = 0; i < tokens.length; i++) {
5443                        String token = tokens[i];
5444                        if (token != null) {
5445                            token = token.trim();
5446                        }
5447                        if (TextUtils.isEmpty(token)) {
5448                            continue;
5449                        }
5450                        pkgs.add(token);
5451                    }
5452                }
5453            } finally {
5454                Binder.restoreCallingIdentity(identity);
5455            }
5456            return pkgs;
5457        }
5458
5459        public String[] getRequestingPackages() throws RemoteException {
5460            final ParceledListSlice list = mPackageManager
5461                    .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
5462                            ActivityManager.getCurrentUser());
5463            final List<PackageInfo> pkgs = list.getList();
5464            if (pkgs == null || pkgs.isEmpty()) return new String[0];
5465            final int N = pkgs.size();
5466            final String[] rt = new String[N];
5467            for (int i = 0; i < N; i++) {
5468                rt[i] = pkgs.get(i).packageName;
5469            }
5470            return rt;
5471        }
5472    }
5473}
5474