2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.notification;
19 import static android.app.NotificationManager.IMPORTANCE_MIN;
20 import static android.app.NotificationManager.IMPORTANCE_NONE;
21 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
22 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
23 import static android.service.notification.NotificationListenerService
24 .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
25 import static android.service.notification.NotificationListenerService
26 .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
27 import static android.service.notification.NotificationListenerService
28 .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
29 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
30 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
31 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
32 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
33 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
34 import static android.service.notification.NotificationListenerService.REASON_CLICK;
35 import static android.service.notification.NotificationListenerService.REASON_ERROR;
36 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
37 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
38 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
39 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
40 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
41 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
42 import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
43 import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
44 import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
45 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
46 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
47 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
48 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
49 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
50 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
51 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
52 import static android.service.notification.NotificationListenerService.TRIM_FULL;
53 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
55 import static android.view.Display.DEFAULT_DISPLAY;
56 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
57 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
59 import android.Manifest;
60 import android.annotation.NonNull;
61 import android.annotation.Nullable;
62 import android.app.ActivityManager;
63 import android.app.ActivityManagerInternal;
64 import android.app.AlarmManager;
65 import android.app.AppGlobals;
66 import android.app.AppOpsManager;
67 import android.app.AutomaticZenRule;
68 import android.app.NotificationChannelGroup;
69 import android.app.backup.BackupManager;
70 import android.app.IActivityManager;
71 import android.app.INotificationManager;
72 import android.app.ITransientNotification;
73 import android.app.Notification;
74 import android.app.NotificationChannel;
75 import android.app.NotificationManager.Policy;
76 import android.app.NotificationManager;
77 import android.app.PendingIntent;
78 import android.app.StatusBarManager;
79 import android.app.usage.UsageEvents;
80 import android.app.usage.UsageStatsManagerInternal;
81 import android.companion.ICompanionDeviceManager;
82 import android.content.BroadcastReceiver;
83 import android.content.ComponentName;
84 import android.content.ContentResolver;
85 import android.content.Context;
86 import android.content.Intent;
87 import android.content.IntentFilter;
88 import android.content.pm.ApplicationInfo;
89 import android.content.pm.IPackageManager;
90 import android.content.pm.PackageInfo;
91 import android.content.pm.PackageManager;
92 import android.content.pm.PackageManager.NameNotFoundException;
93 import android.content.pm.ParceledListSlice;
94 import android.content.res.Resources;
95 import android.database.ContentObserver;
96 import android.media.AudioManager;
97 import android.media.AudioManagerInternal;
98 import android.media.IRingtonePlayer;
99 import android.media.ToneGenerator;
100 import android.net.Uri;
101 import android.os.Binder;
102 import android.os.Build;
103 import android.os.Bundle;
104 import android.os.Environment;
105 import android.os.Handler;
106 import android.os.HandlerThread;
107 import android.os.IBinder;
108 import android.os.IInterface;
109 import android.os.Looper;
110 import android.os.Message;
111 import android.os.Process;
112 import android.os.RemoteException;
113 import android.os.ServiceManager;
114 import android.os.SystemClock;
115 import android.os.SystemProperties;
116 import android.os.UserHandle;
117 import android.os.Vibrator;
118 import android.os.VibrationEffect;
119 import android.provider.Settings;
120 import android.service.notification.Adjustment;
121 import android.service.notification.Condition;
122 import android.service.notification.IConditionProvider;
123 import android.service.notification.INotificationListener;
124 import android.service.notification.IStatusBarNotificationHolder;
125 import android.service.notification.NotificationAssistantService;
126 import android.service.notification.NotificationListenerService;
127 import android.service.notification.NotificationRankingUpdate;
128 import android.service.notification.NotificationRecordProto;
129 import android.service.notification.NotificationServiceDumpProto;
130 import android.service.notification.NotificationServiceProto;
131 import android.service.notification.SnoozeCriterion;
132 import android.service.notification.StatusBarNotification;
133 import android.service.notification.ZenModeConfig;
134 import android.service.notification.ZenModeProto;
135 import android.telephony.PhoneStateListener;
136 import android.telephony.TelephonyManager;
137 import android.text.TextUtils;
138 import android.util.ArrayMap;
139 import android.util.ArraySet;
140 import android.util.AtomicFile;
141 import android.util.Log;
142 import android.util.Slog;
143 import android.util.SparseArray;
144 import android.util.Xml;
145 import android.util.proto.ProtoOutputStream;
146 import android.view.WindowManagerInternal;
147 import android.view.accessibility.AccessibilityEvent;
148 import android.view.accessibility.AccessibilityManager;
149 import android.widget.Toast;
151 import com.android.internal.R;
152 import com.android.internal.annotations.GuardedBy;
153 import com.android.internal.annotations.VisibleForTesting;
154 import com.android.internal.logging.MetricsLogger;
155 import com.android.internal.logging.nano.MetricsProto;
156 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
157 import com.android.internal.statusbar.NotificationVisibility;
158 import com.android.internal.util.ArrayUtils;
159 import com.android.internal.util.DumpUtils;
160 import com.android.internal.util.FastXmlSerializer;
161 import com.android.internal.util.Preconditions;
162 import com.android.server.DeviceIdleController;
163 import com.android.server.EventLogTags;
164 import com.android.server.LocalServices;
165 import com.android.server.SystemService;
166 import com.android.server.lights.Light;
167 import com.android.server.lights.LightsManager;
168 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
169 import com.android.server.policy.PhoneWindowManager;
170 import com.android.server.statusbar.StatusBarManagerInternal;
171 import com.android.server.notification.ManagedServices.UserProfiles;
173 import libcore.io.IoUtils;
175 import org.json.JSONException;
176 import org.json.JSONObject;
177 import org.xmlpull.v1.XmlPullParser;
178 import org.xmlpull.v1.XmlPullParserException;
179 import org.xmlpull.v1.XmlSerializer;
181 import java.io.ByteArrayInputStream;
182 import java.io.ByteArrayOutputStream;
184 import java.io.FileDescriptor;
185 import java.io.FileInputStream;
186 import java.io.FileNotFoundException;
187 import java.io.FileOutputStream;
188 import java.io.IOException;
189 import java.io.InputStream;
190 import java.io.OutputStream;
191 import java.io.PrintWriter;
192 import java.nio.charset.StandardCharsets;
193 import java.util.ArrayDeque;
194 import java.util.ArrayList;
195 import java.util.Arrays;
196 import java.util.Iterator;
197 import java.util.List;
198 import java.util.Map;
199 import java.util.Map.Entry;
200 import java.util.Objects;
201 import java.util.concurrent.TimeUnit;
204 public class NotificationManagerService extends SystemService {
205 static final String TAG = "NotificationService";
206 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
207 public static final boolean ENABLE_CHILD_NOTIFICATIONS
208 = SystemProperties.getBoolean("debug.child_notifs", true);
210 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
211 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 10f;
214 static final int MESSAGE_TIMEOUT = 2;
215 static final int MESSAGE_SAVE_POLICY_FILE = 3;
216 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
217 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
218 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
220 // ranking thread messages
221 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
222 private static final int MESSAGE_RANKING_SORT = 1001;
224 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
225 static final int SHORT_DELAY = 2000; // 2 seconds
227 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
229 static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
231 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
233 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
235 static final boolean ENABLE_BLOCKED_TOASTS = true;
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.
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;
247 /** notification_enqueue status value for a newly enqueued notification. */
248 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
250 /** notification_enqueue status value for an existing notification. */
251 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
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
257 private static final long DELAY_FOR_ASSISTANT_TIME = 100;
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";
265 private IActivityManager mAm;
266 private IPackageManager mPackageManager;
267 private PackageManager mPackageManagerClient;
268 AudioManager mAudioManager;
269 AudioManagerInternal mAudioManagerInternal;
270 @Nullable StatusBarManagerInternal mStatusBar;
272 private WindowManagerInternal mWindowManagerInternal;
273 private AlarmManager mAlarmManager;
274 private ICompanionDeviceManager mCompanionManager;
276 final IBinder mForegroundToken = new Binder();
277 private Handler mHandler;
278 private final HandlerThread mRankingThread = new HandlerThread("ranker",
279 Process.THREAD_PRIORITY_BACKGROUND);
281 private Light mNotificationLight;
282 Light mAttentionLight;
284 private long[] mFallbackVibrationPattern;
285 private boolean mUseAttentionLight;
286 boolean mSystemReady;
288 private boolean mDisableNotificationEffects;
289 private int mCallState;
290 private String mSoundNotificationKey;
291 private String mVibrateNotificationKey;
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;
299 // for enabling and disabling notification pulse behavior
300 private boolean mScreenOn = true;
301 private boolean mInCall = false;
302 private boolean mNotificationPulseEnabled;
304 // for generating notification tones in-call
305 private ToneGenerator mInCallToneGenerator;
306 private final Object mInCallToneGeneratorLock = new Object();
308 // used as a mutex for access to all active notifications & listeners
309 final Object mNotificationLock = new Object();
310 @GuardedBy("mNotificationLock")
311 final ArrayList<NotificationRecord> mNotificationList =
312 new ArrayList<NotificationRecord>();
313 @GuardedBy("mNotificationLock")
314 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
315 new ArrayMap<String, NotificationRecord>();
316 @GuardedBy("mNotificationLock")
317 final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
318 @GuardedBy("mNotificationLock")
319 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
320 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
321 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
322 final PolicyAccess mPolicyAccess = new PolicyAccess();
324 // The last key in this list owns the hardware.
325 ArrayList<String> mLights = new ArrayList<>();
327 private AppOpsManager mAppOps;
328 private UsageStatsManagerInternal mAppUsageStats;
330 private Archive mArchive;
332 // Persistent storage for notification policy
333 private AtomicFile mPolicyFile;
335 private static final int DB_VERSION = 1;
337 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
338 private static final String ATTR_VERSION = "version";
340 private RankingHelper mRankingHelper;
342 private final UserProfiles mUserProfiles = new UserProfiles();
343 private NotificationListeners mListeners;
344 private NotificationAssistants mNotificationAssistants;
345 private ConditionProviders mConditionProviders;
346 private NotificationUsageStats mUsageStats;
348 private static final int MY_UID = Process.myUid();
349 private static final int MY_PID = Process.myPid();
350 private static final IBinder WHITELIST_TOKEN = new Binder();
351 private RankingHandler mRankingHandler;
352 private long mLastOverRateLogTime;
353 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
355 private SnoozeHelper mSnoozeHelper;
356 private GroupHelper mGroupHelper;
357 private boolean mIsTelevision;
359 private static class Archive {
360 final int mBufferSize;
361 final ArrayDeque<StatusBarNotification> mBuffer;
363 public Archive(int size) {
365 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
368 public String toString() {
369 final StringBuilder sb = new StringBuilder();
370 final int N = mBuffer.size();
371 sb.append("Archive (");
373 sb.append(" notification");
374 sb.append((N==1)?")":"s)");
375 return sb.toString();
378 public void record(StatusBarNotification nr) {
379 if (mBuffer.size() == mBufferSize) {
380 mBuffer.removeFirst();
383 // We don't want to store the heavy bits of the notification in the archive,
384 // but other clients in the system process might be using the object, so we
385 // store a (lightened) copy.
386 mBuffer.addLast(nr.cloneLight());
389 public Iterator<StatusBarNotification> descendingIterator() {
390 return mBuffer.descendingIterator();
393 public StatusBarNotification[] getArray(int count) {
394 if (count == 0) count = mBufferSize;
395 final StatusBarNotification[] a
396 = new StatusBarNotification[Math.min(count, mBuffer.size())];
397 Iterator<StatusBarNotification> iter = descendingIterator();
399 while (iter.hasNext() && i < count) {
400 a[i++] = iter.next();
407 private void readPolicyXml(InputStream stream, boolean forRestore)
408 throws XmlPullParserException, NumberFormatException, IOException {
409 final XmlPullParser parser = Xml.newPullParser();
410 parser.setInput(stream, StandardCharsets.UTF_8.name());
412 while (parser.next() != END_DOCUMENT) {
413 mZenModeHelper.readXml(parser, forRestore);
414 mRankingHelper.readXml(parser, forRestore);
418 private void loadPolicyFile() {
419 if (DBG) Slog.d(TAG, "loadPolicyFile");
420 synchronized (mPolicyFile) {
422 FileInputStream infile = null;
424 infile = mPolicyFile.openRead();
425 readPolicyXml(infile, false /*forRestore*/);
426 } catch (FileNotFoundException e) {
428 } catch (IOException e) {
429 Log.wtf(TAG, "Unable to read notification policy", e);
430 } catch (NumberFormatException e) {
431 Log.wtf(TAG, "Unable to parse notification policy", e);
432 } catch (XmlPullParserException e) {
433 Log.wtf(TAG, "Unable to parse notification policy", e);
435 IoUtils.closeQuietly(infile);
440 public void savePolicyFile() {
441 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
442 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
445 private void handleSavePolicyFile() {
446 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
447 synchronized (mPolicyFile) {
448 final FileOutputStream stream;
450 stream = mPolicyFile.startWrite();
451 } catch (IOException e) {
452 Slog.w(TAG, "Failed to save policy file", e);
457 writePolicyXml(stream, false /*forBackup*/);
458 mPolicyFile.finishWrite(stream);
459 } catch (IOException e) {
460 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
461 mPolicyFile.failWrite(stream);
464 BackupManager.dataChanged(getContext().getPackageName());
467 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
468 final XmlSerializer out = new FastXmlSerializer();
469 out.setOutput(stream, StandardCharsets.UTF_8.name());
470 out.startDocument(null, true);
471 out.startTag(null, TAG_NOTIFICATION_POLICY);
472 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
473 mZenModeHelper.writeXml(out, forBackup);
474 mRankingHelper.writeXml(out, forBackup);
475 out.endTag(null, TAG_NOTIFICATION_POLICY);
479 /** Use this to check if a package can post a notification or toast. */
480 private boolean checkNotificationOp(String pkg, int uid) {
481 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
482 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
485 private static final class ToastRecord
489 final ITransientNotification callback;
493 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
497 this.callback = callback;
498 this.duration = duration;
502 void update(int duration) {
503 this.duration = duration;
506 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
507 if (filter != null && !filter.matches(pkg)) return;
508 pw.println(prefix + this);
512 public final String toString()
514 return "ToastRecord{"
515 + Integer.toHexString(System.identityHashCode(this))
517 + " callback=" + callback
518 + " duration=" + duration;
523 final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
526 public void onSetDisabled(int status) {
527 synchronized (mNotificationLock) {
528 mDisableNotificationEffects =
529 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
530 if (disableNotificationEffects(null) != null) {
531 // cancel whatever's going on
532 long identity = Binder.clearCallingIdentity();
534 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
535 if (player != null) {
538 } catch (RemoteException e) {
540 Binder.restoreCallingIdentity(identity);
543 identity = Binder.clearCallingIdentity();
547 Binder.restoreCallingIdentity(identity);
554 public void onClearAll(int callingUid, int callingPid, int userId) {
555 synchronized (mNotificationLock) {
556 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
557 /*includeCurrentProfiles*/ true);
562 public void onNotificationClick(int callingUid, int callingPid, String key) {
563 synchronized (mNotificationLock) {
564 NotificationRecord r = mNotificationsByKey.get(key);
566 Log.w(TAG, "No notification with key: " + key);
569 final long now = System.currentTimeMillis();
570 MetricsLogger.action(r.getLogMaker(now)
571 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
572 .setType(MetricsEvent.TYPE_ACTION));
573 EventLogTags.writeNotificationClicked(key,
574 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
576 StatusBarNotification sbn = r.sbn;
577 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
578 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
579 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
585 public void onNotificationActionClick(int callingUid, int callingPid, String key,
587 synchronized (mNotificationLock) {
588 NotificationRecord r = mNotificationsByKey.get(key);
590 Log.w(TAG, "No notification with key: " + key);
593 final long now = System.currentTimeMillis();
594 MetricsLogger.action(r.getLogMaker(now)
595 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
596 .setType(MetricsEvent.TYPE_ACTION)
597 .setSubtype(actionIndex));
598 EventLogTags.writeNotificationActionClicked(key, actionIndex,
599 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
600 // TODO: Log action click via UsageStats.
605 public void onNotificationClear(int callingUid, int callingPid,
606 String pkg, String tag, int id, int userId) {
607 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
608 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
609 true, userId, REASON_CANCEL, null);
613 public void onPanelRevealed(boolean clearEffects, int items) {
614 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
615 MetricsLogger.histogram(getContext(), "note_load", items);
616 EventLogTags.writeNotificationPanelRevealed(items);
623 public void onPanelHidden() {
624 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
625 EventLogTags.writeNotificationPanelHidden();
629 public void clearEffects() {
630 synchronized (mNotificationLock) {
631 if (DBG) Slog.d(TAG, "clearEffects");
633 clearVibrateLocked();
639 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
640 int uid, int initialPid, String message, int userId) {
641 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
642 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
643 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
645 long ident = Binder.clearCallingIdentity();
647 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
648 "Bad notification posted from package " + pkg
650 } catch (RemoteException e) {
652 Binder.restoreCallingIdentity(ident);
656 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
657 NotificationVisibility[] noLongerVisibleKeys) {
658 synchronized (mNotificationLock) {
659 for (NotificationVisibility nv : newlyVisibleKeys) {
660 NotificationRecord r = mNotificationsByKey.get(nv.key);
661 if (r == null) continue;
662 r.setVisibility(true, nv.rank);
665 // Note that we might receive this event after notifications
666 // have already left the system, e.g. after dismissing from the
667 // shade. Hence not finding notifications in
668 // mNotificationsByKey is not an exceptional condition.
669 for (NotificationVisibility nv : noLongerVisibleKeys) {
670 NotificationRecord r = mNotificationsByKey.get(nv.key);
671 if (r == null) continue;
672 r.setVisibility(false, nv.rank);
679 public void onNotificationExpansionChanged(String key,
680 boolean userAction, boolean expanded) {
681 synchronized (mNotificationLock) {
682 NotificationRecord r = mNotificationsByKey.get(key);
684 r.stats.onExpansionChanged(userAction, expanded);
685 final long now = System.currentTimeMillis();
686 MetricsLogger.action(r.getLogMaker(now)
687 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
688 .setType(MetricsEvent.TYPE_DETAIL));
689 EventLogTags.writeNotificationExpansion(key,
690 userAction ? 1 : 0, expanded ? 1 : 0,
691 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
697 @GuardedBy("mNotificationLock")
698 private void clearSoundLocked() {
699 mSoundNotificationKey = null;
700 long identity = Binder.clearCallingIdentity();
702 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
703 if (player != null) {
706 } catch (RemoteException e) {
708 Binder.restoreCallingIdentity(identity);
712 @GuardedBy("mNotificationLock")
713 private void clearVibrateLocked() {
714 mVibrateNotificationKey = null;
715 long identity = Binder.clearCallingIdentity();
719 Binder.restoreCallingIdentity(identity);
723 @GuardedBy("mNotificationLock")
724 private void clearLightsLocked() {
727 updateLightsLocked();
730 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
732 public void onReceive(Context context, Intent intent) {
733 String action = intent.getAction();
734 if (action == null) {
737 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
738 final NotificationRecord record;
739 synchronized (mNotificationLock) {
740 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
742 if (record != null) {
743 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
744 record.sbn.getPackageName(), record.sbn.getTag(),
745 record.sbn.getId(), 0,
746 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
747 REASON_TIMEOUT, null);
753 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
755 public void onReceive(Context context, Intent intent) {
756 String action = intent.getAction();
757 if (action == null) {
761 boolean queryRestart = false;
762 boolean queryRemove = false;
763 boolean packageChanged = false;
764 boolean cancelNotifications = true;
765 int reason = REASON_PACKAGE_CHANGED;
767 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
768 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
769 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
770 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
771 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
772 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
773 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
774 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
775 UserHandle.USER_ALL);
776 String pkgList[] = null;
777 int uidList[] = null;
778 boolean removingPackage = queryRemove &&
779 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
780 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
781 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
782 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
783 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
784 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
785 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
786 reason = REASON_PACKAGE_SUSPENDED;
787 } else if (queryRestart) {
788 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
789 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
791 Uri uri = intent.getData();
795 String pkgName = uri.getSchemeSpecificPart();
796 if (pkgName == null) {
799 if (packageChanged) {
800 // We cancel notifications for packages which have just been disabled
802 final int enabled = mPackageManager.getApplicationEnabledSetting(
804 changeUserId != UserHandle.USER_ALL ? changeUserId :
805 UserHandle.USER_SYSTEM);
806 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
807 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
808 cancelNotifications = false;
810 } catch (IllegalArgumentException e) {
811 // Package doesn't exist; probably racing with uninstall.
812 // cancelNotifications is already true, so nothing to do here.
814 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
816 } catch (RemoteException e) {
817 // Failed to talk to PackageManagerService Should never happen!
820 pkgList = new String[]{pkgName};
821 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
823 if (pkgList != null && (pkgList.length > 0)) {
824 for (String pkgName : pkgList) {
825 if (cancelNotifications) {
826 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
827 !queryRestart, changeUserId, reason, null);
831 mListeners.onPackagesChanged(removingPackage, pkgList);
832 mNotificationAssistants.onPackagesChanged(removingPackage, pkgList);
833 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
834 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
840 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
842 public void onReceive(Context context, Intent intent) {
843 String action = intent.getAction();
845 if (action.equals(Intent.ACTION_SCREEN_ON)) {
846 // Keep track of screen on/off state, but do not turn off the notification light
847 // until user passes through the lock screen or views the notification.
849 updateNotificationPulse();
850 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
852 updateNotificationPulse();
853 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
854 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
855 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
856 updateNotificationPulse();
857 synchronized (mInCallToneGeneratorLock) {
859 if (mInCallToneGenerator == null) {
860 int relativeToneVolume = getContext().getResources().getInteger(
861 R.integer.config_inCallNotificationVolumeRelative);
862 if (relativeToneVolume < ToneGenerator.MIN_VOLUME
863 || relativeToneVolume > ToneGenerator.MAX_VOLUME) {
864 relativeToneVolume = ToneGenerator.MAX_VOLUME;
867 mInCallToneGenerator = new ToneGenerator(
868 AudioManager.STREAM_VOICE_CALL, relativeToneVolume);
869 } catch (RuntimeException e) {
870 Log.e(TAG, "Error creating local tone generator: " + e);
871 mInCallToneGenerator = null;
875 if (mInCallToneGenerator != null) {
876 mInCallToneGenerator.release();
877 mInCallToneGenerator = null;
881 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
882 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
883 if (userHandle >= 0) {
884 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
885 REASON_USER_STOPPED, null);
887 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
888 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
889 if (userHandle >= 0) {
890 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
891 REASON_PROFILE_TURNED_OFF, null);
893 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
894 // turn off LED when user passes through lock screen
895 mNotificationLight.turnOff();
896 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
897 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
898 // reload per-user settings
899 mSettingsObserver.update(null);
900 mUserProfiles.updateCache(context);
901 // Refresh managed services
902 mConditionProviders.onUserSwitched(user);
903 mListeners.onUserSwitched(user);
904 mNotificationAssistants.onUserSwitched(user);
905 mZenModeHelper.onUserSwitched(user);
906 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
907 mUserProfiles.updateCache(context);
908 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
909 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
910 mZenModeHelper.onUserRemoved(user);
911 mRankingHelper.onUserRemoved(user);
913 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
914 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
915 mConditionProviders.onUserUnlocked(user);
916 mListeners.onUserUnlocked(user);
917 mNotificationAssistants.onUserUnlocked(user);
918 mZenModeHelper.onUserUnlocked(user);
923 private final class SettingsObserver extends ContentObserver {
924 private final Uri NOTIFICATION_BADGING_URI
925 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
926 private final Uri NOTIFICATION_LIGHT_PULSE_URI
927 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
928 private final Uri NOTIFICATION_RATE_LIMIT_URI
929 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
931 SettingsObserver(Handler handler) {
936 ContentResolver resolver = getContext().getContentResolver();
937 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
938 false, this, UserHandle.USER_ALL);
939 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
940 false, this, UserHandle.USER_ALL);
941 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
942 false, this, UserHandle.USER_ALL);
946 @Override public void onChange(boolean selfChange, Uri uri) {
950 public void update(Uri uri) {
951 ContentResolver resolver = getContext().getContentResolver();
952 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
953 boolean pulseEnabled = Settings.System.getInt(resolver,
954 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
955 if (mNotificationPulseEnabled != pulseEnabled) {
956 mNotificationPulseEnabled = pulseEnabled;
957 updateNotificationPulse();
960 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
961 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
962 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
964 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
965 mRankingHelper.updateBadgingEnabled();
970 private SettingsObserver mSettingsObserver;
971 private ZenModeHelper mZenModeHelper;
973 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
974 int[] ar = r.getIntArray(resid);
978 final int len = ar.length > maxlen ? maxlen : ar.length;
979 long[] out = new long[len];
980 for (int i=0; i<len; i++) {
986 public NotificationManagerService(Context context) {
988 Notification.processWhitelistToken = WHITELIST_TOKEN;
991 // TODO - replace these methods with a single VisibleForTesting constructor
993 void setAudioManager(AudioManager audioMananger) {
994 mAudioManager = audioMananger;
998 void setVibrator(Vibrator vibrator) {
999 mVibrator = vibrator;
1003 void setLights(Light light) {
1004 mNotificationLight = light;
1005 mAttentionLight = light;
1006 mNotificationPulseEnabled = true;
1010 void setScreenOn(boolean on) {
1015 int getNotificationRecordCount() {
1016 synchronized (mNotificationLock) {
1017 int count = mNotificationList.size() + mNotificationsByKey.size()
1018 + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
1019 // subtract duplicates
1020 for (NotificationRecord posted : mNotificationList) {
1021 if (mNotificationsByKey.containsKey(posted.getKey())) {
1024 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
1034 void addNotification(NotificationRecord r) {
1035 mNotificationList.add(r);
1036 mNotificationsByKey.put(r.sbn.getKey(), r);
1037 if (r.sbn.isGroup()) {
1038 mSummaryByGroupKey.put(r.getGroupKey(), r);
1043 void addEnqueuedNotification(NotificationRecord r) {
1044 mEnqueuedNotifications.add(r);
1048 void setSystemReady(boolean systemReady) {
1049 mSystemReady = systemReady;
1053 void setHandler(Handler handler) {
1058 void setFallbackVibrationPattern(long[] vibrationPattern) {
1059 mFallbackVibrationPattern = vibrationPattern;
1063 void setPackageManager(IPackageManager packageManager) {
1064 mPackageManager = packageManager;
1068 void setRankingHelper(RankingHelper rankingHelper) {
1069 mRankingHelper = rankingHelper;
1073 void setIsTelevision(boolean isTelevision) {
1074 mIsTelevision = isTelevision;
1077 // TODO: Tests should call onStart instead once the methods above are removed.
1079 void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
1080 LightsManager lightsManager, NotificationListeners notificationListeners,
1081 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1082 NotificationUsageStats usageStats) {
1083 Resources resources = getContext().getResources();
1084 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1085 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1086 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1088 mAm = ActivityManager.getService();
1089 mPackageManager = packageManager;
1090 mPackageManagerClient = packageManagerClient;
1091 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1092 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1093 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1094 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1095 mCompanionManager = companionManager;
1097 mHandler = new WorkerHandler(looper);
1098 mRankingThread.start();
1099 String[] extractorNames;
1101 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1102 } catch (Resources.NotFoundException e) {
1103 extractorNames = new String[0];
1105 mUsageStats = usageStats;
1106 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1107 mRankingHelper = new RankingHelper(getContext(),
1108 getContext().getPackageManager(),
1112 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
1113 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1114 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1116 public void onConfigChanged() {
1121 void onZenModeChanged() {
1122 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1123 getContext().sendBroadcastAsUser(
1124 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1125 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1126 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1127 synchronized (mNotificationLock) {
1128 updateInterruptionFilterLocked();
1133 void onPolicyChanged() {
1134 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1137 mSnoozeHelper = snoozeHelper;
1138 mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
1140 public void addAutoGroup(String key) {
1141 synchronized (mNotificationLock) {
1142 addAutogroupKeyLocked(key);
1144 mRankingHandler.requestSort(false);
1148 public void removeAutoGroup(String key) {
1149 synchronized (mNotificationLock) {
1150 removeAutogroupKeyLocked(key);
1152 mRankingHandler.requestSort(false);
1156 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1157 createAutoGroupSummary(userId, pkg, triggeringKey);
1161 public void removeAutoGroupSummary(int userId, String pkg) {
1162 synchronized (mNotificationLock) {
1163 clearAutogroupSummaryLocked(userId, pkg);
1168 final File systemDir = new File(Environment.getDataDirectory(), "system");
1169 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
1173 // This is a ManagedServices object that keeps track of the listeners.
1174 mListeners = notificationListeners;
1176 // This is a MangedServices object that keeps track of the assistant.
1177 mNotificationAssistants = new NotificationAssistants();
1179 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1180 if (mStatusBar != null) {
1181 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1184 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1185 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1187 mFallbackVibrationPattern = getLongArray(resources,
1188 R.array.config_notificationFallbackVibePattern,
1189 VIBRATE_PATTERN_MAXLEN,
1190 DEFAULT_VIBRATE_PATTERN);
1192 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1194 // Don't start allowing notifications until the setup wizard has run once.
1195 // After that, including subsequent boots, init with notifications turned on.
1196 // This works on the first boot because the setup wizard will toggle this
1197 // flag at least once and we'll go back to 0 after that.
1198 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1199 Settings.Global.DEVICE_PROVISIONED, 0)) {
1200 mDisableNotificationEffects = true;
1202 mZenModeHelper.initZenMode();
1203 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1205 mUserProfiles.updateCache(getContext());
1206 listenForCallState();
1208 // register for various Intents
1209 IntentFilter filter = new IntentFilter();
1210 filter.addAction(Intent.ACTION_SCREEN_ON);
1211 filter.addAction(Intent.ACTION_SCREEN_OFF);
1212 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1213 filter.addAction(Intent.ACTION_USER_PRESENT);
1214 filter.addAction(Intent.ACTION_USER_STOPPED);
1215 filter.addAction(Intent.ACTION_USER_SWITCHED);
1216 filter.addAction(Intent.ACTION_USER_ADDED);
1217 filter.addAction(Intent.ACTION_USER_REMOVED);
1218 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1219 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1220 getContext().registerReceiver(mIntentReceiver, filter);
1222 IntentFilter pkgFilter = new IntentFilter();
1223 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1224 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1225 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1226 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1227 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1228 pkgFilter.addDataScheme("package");
1229 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1232 IntentFilter suspendedPkgFilter = new IntentFilter();
1233 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1234 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1235 suspendedPkgFilter, null, null);
1237 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1238 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1241 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1242 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1243 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1245 mSettingsObserver = new SettingsObserver(mHandler);
1247 mArchive = new Archive(resources.getInteger(
1248 R.integer.config_notificationServiceArchiveSize));
1250 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1251 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1255 public void onStart() {
1256 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1258 public void repost(int userId, NotificationRecord r) {
1261 Slog.d(TAG, "Reposting " + r.getKey());
1263 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1264 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1265 r.sbn.getNotification(), userId);
1266 } catch (Exception e) {
1267 Slog.e(TAG, "Cannot un-snooze notification", e);
1272 init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
1273 getLocalService(LightsManager.class), new NotificationListeners(),
1274 null, snoozeHelper, new NotificationUsageStats(getContext()));
1275 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1276 publishLocalService(NotificationManagerInternal.class, mInternalService);
1279 private void sendRegisteredOnlyBroadcast(String action) {
1280 getContext().sendBroadcastAsUser(new Intent(action)
1281 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1285 public void onBootPhase(int phase) {
1286 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1287 // no beeping until we're basically done booting
1288 mSystemReady = true;
1290 // Grab our optional AudioService
1291 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1292 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1293 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1294 mZenModeHelper.onSystemReady();
1295 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1296 // This observer will force an update when observe is called, causing us to
1297 // bind to listener services.
1298 mSettingsObserver.observe();
1299 mListeners.onBootPhaseAppsCanStart();
1300 mNotificationAssistants.onBootPhaseAppsCanStart();
1301 mConditionProviders.onBootPhaseAppsCanStart();
1305 @GuardedBy("mNotificationLock")
1306 private void updateListenerHintsLocked() {
1307 final int hints = calculateHints();
1308 if (hints == mListenerHints) return;
1309 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1310 mListenerHints = hints;
1311 scheduleListenerHintsChanged(hints);
1314 @GuardedBy("mNotificationLock")
1315 private void updateEffectsSuppressorLocked() {
1316 final long updatedSuppressedEffects = calculateSuppressedEffects();
1317 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1318 final List<ComponentName> suppressors = getSuppressors();
1319 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1320 mEffectsSuppressors = suppressors;
1321 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1322 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1325 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1326 boolean fromListener) {
1327 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1329 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1330 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1333 mRankingHelper.updateNotificationChannel(pkg, uid, channel);
1335 final NotificationChannel modifiedChannel =
1336 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1338 if (!fromListener) {
1339 mListeners.notifyNotificationChannelChanged(
1340 pkg, UserHandle.getUserHandleForUid(uid),
1341 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1344 synchronized (mNotificationLock) {
1345 final int N = mNotificationList.size();
1346 for (int i = N - 1; i >= 0; --i) {
1347 NotificationRecord r = mNotificationList.get(i);
1348 if (r.sbn.getPackageName().equals(pkg)
1349 && r.sbn.getUid() == uid
1350 && channel.getId() != null
1351 && channel.getId().equals(r.getChannel().getId())) {
1352 r.updateNotificationChannel(modifiedChannel);
1356 mRankingHandler.requestSort(true);
1360 private ArrayList<ComponentName> getSuppressors() {
1361 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1362 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1363 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1365 for (ManagedServiceInfo info : serviceInfoList) {
1366 names.add(info.component);
1373 private boolean removeDisabledHints(ManagedServiceInfo info) {
1374 return removeDisabledHints(info, 0);
1377 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1378 boolean removed = false;
1380 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1381 final int hint = mListenersDisablingEffects.keyAt(i);
1382 final ArraySet<ManagedServiceInfo> listeners =
1383 mListenersDisablingEffects.valueAt(i);
1385 if (hints == 0 || (hint & hints) == hint) {
1386 removed = removed || listeners.remove(info);
1393 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1394 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1395 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1398 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1399 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1402 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1403 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1407 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1408 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1409 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1412 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1413 hintListeners.add(info);
1416 private int calculateHints() {
1418 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1419 int hint = mListenersDisablingEffects.keyAt(i);
1420 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1422 if (!serviceInfoList.isEmpty()) {
1430 private long calculateSuppressedEffects() {
1431 int hints = calculateHints();
1432 long suppressedEffects = 0;
1434 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1435 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1438 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1439 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1442 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1443 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1446 return suppressedEffects;
1449 @GuardedBy("mNotificationLock")
1450 private void updateInterruptionFilterLocked() {
1451 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1452 if (interruptionFilter == mInterruptionFilter) return;
1453 mInterruptionFilter = interruptionFilter;
1454 scheduleInterruptionFilterChanged(interruptionFilter);
1458 INotificationManager getBinderService() {
1459 return INotificationManager.Stub.asInterface(mService);
1463 NotificationManagerInternal getInternalService() {
1464 return mInternalService;
1467 private final IBinder mService = new INotificationManager.Stub() {
1469 // ============================================================================
1472 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1475 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1476 + " duration=" + duration);
1479 if (pkg == null || callback == null) {
1480 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1484 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1485 final boolean isPackageSuspended =
1486 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1488 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1489 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1490 || isPackageSuspended)) {
1491 Slog.e(TAG, "Suppressing toast from package " + pkg
1492 + (isPackageSuspended
1493 ? " due to package suspended by administrator."
1494 : " by user request."));
1498 synchronized (mToastQueue) {
1499 int callingPid = Binder.getCallingPid();
1500 long callingId = Binder.clearCallingIdentity();
1503 int index = indexOfToastLocked(pkg, callback);
1504 // If it's already in the queue, we update it in place, we don't
1505 // move it to the end of the queue.
1507 record = mToastQueue.get(index);
1508 record.update(duration);
1510 // Limit the number of toasts that any given package except the android
1511 // package can enqueue. Prevents DOS attacks and deals with leaks.
1512 if (!isSystemToast) {
1514 final int N = mToastQueue.size();
1515 for (int i=0; i<N; i++) {
1516 final ToastRecord r = mToastQueue.get(i);
1517 if (r.pkg.equals(pkg)) {
1519 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1520 Slog.e(TAG, "Package has already posted " + count
1521 + " toasts. Not showing more. Package=" + pkg);
1528 Binder token = new Binder();
1529 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1530 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1531 mToastQueue.add(record);
1532 index = mToastQueue.size() - 1;
1533 keepProcessAliveIfNeededLocked(callingPid);
1535 // If it's at index 0, it's the current toast. It doesn't matter if it's
1536 // new or just been updated. Call back and tell it to show itself.
1537 // If the callback fails, this will remove it from the list, so don't
1538 // assume that it's valid after this.
1540 showNextToastLocked();
1543 Binder.restoreCallingIdentity(callingId);
1549 public void cancelToast(String pkg, ITransientNotification callback) {
1550 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1552 if (pkg == null || callback == null) {
1553 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1557 synchronized (mToastQueue) {
1558 long callingId = Binder.clearCallingIdentity();
1560 int index = indexOfToastLocked(pkg, callback);
1562 cancelToastLocked(index);
1564 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1565 + " callback=" + callback);
1568 Binder.restoreCallingIdentity(callingId);
1574 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1575 Notification notification, int userId) throws RemoteException {
1576 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1577 Binder.getCallingPid(), tag, id, notification, userId);
1581 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1582 checkCallerIsSystemOrSameApp(pkg);
1583 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1584 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1585 // Don't allow client applications to cancel foreground service notis or autobundled
1587 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1588 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1589 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1590 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1594 public void cancelAllNotifications(String pkg, int userId) {
1595 checkCallerIsSystemOrSameApp(pkg);
1597 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1598 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1600 // Calling from user space, don't allow the canceling of actively
1601 // running foreground services.
1602 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1603 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1604 REASON_APP_CANCEL_ALL, null);
1608 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1609 checkCallerIsSystem();
1611 mRankingHelper.setEnabled(pkg, uid, enabled);
1612 // Now, cancel any outstanding notifications that are part of a just-disabled app
1614 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1615 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1621 * Use this when you just want to know if notifications are OK for this package.
1624 public boolean areNotificationsEnabled(String pkg) {
1625 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1629 * Use this when you just want to know if notifications are OK for this package.
1632 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1633 checkCallerIsSystemOrSameApp(pkg);
1634 if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) {
1635 getContext().enforceCallingPermission(
1636 android.Manifest.permission.INTERACT_ACROSS_USERS,
1637 "canNotifyAsPackage for uid " + uid);
1640 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1644 public int getPackageImportance(String pkg) {
1645 checkCallerIsSystemOrSameApp(pkg);
1646 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1650 public boolean canShowBadge(String pkg, int uid) {
1651 checkCallerIsSystem();
1652 return mRankingHelper.canShowBadge(pkg, uid);
1656 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1657 checkCallerIsSystem();
1658 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1663 public void createNotificationChannelGroups(String pkg,
1664 ParceledListSlice channelGroupList) throws RemoteException {
1665 checkCallerIsSystemOrSameApp(pkg);
1666 List<NotificationChannelGroup> groups = channelGroupList.getList();
1667 final int groupSize = groups.size();
1668 for (int i = 0; i < groupSize; i++) {
1669 final NotificationChannelGroup group = groups.get(i);
1670 Preconditions.checkNotNull(group, "group in list is null");
1671 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1672 true /* fromTargetApp */);
1673 mListeners.notifyNotificationChannelGroupChanged(pkg,
1674 UserHandle.of(UserHandle.getCallingUserId()), group,
1675 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1680 private void createNotificationChannelsImpl(String pkg, int uid,
1681 ParceledListSlice channelsList) {
1682 List<NotificationChannel> channels = channelsList.getList();
1683 final int channelsSize = channels.size();
1684 for (int i = 0; i < channelsSize; i++) {
1685 final NotificationChannel channel = channels.get(i);
1686 Preconditions.checkNotNull(channel, "channel in list is null");
1687 mRankingHelper.createNotificationChannel(pkg, uid, channel,
1688 true /* fromTargetApp */);
1689 mListeners.notifyNotificationChannelChanged(pkg,
1690 UserHandle.getUserHandleForUid(uid),
1691 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1692 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1698 public void createNotificationChannels(String pkg,
1699 ParceledListSlice channelsList) throws RemoteException {
1700 checkCallerIsSystemOrSameApp(pkg);
1701 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1705 public void createNotificationChannelsForPackage(String pkg, int uid,
1706 ParceledListSlice channelsList) throws RemoteException {
1707 checkCallerIsSystem();
1708 createNotificationChannelsImpl(pkg, uid, channelsList);
1712 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1713 checkCallerIsSystemOrSameApp(pkg);
1714 return mRankingHelper.getNotificationChannel(
1715 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1719 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1720 String channelId, boolean includeDeleted) {
1721 checkCallerIsSystem();
1722 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1726 public void deleteNotificationChannel(String pkg, String channelId) {
1727 checkCallerIsSystemOrSameApp(pkg);
1728 final int callingUid = Binder.getCallingUid();
1729 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1730 throw new IllegalArgumentException("Cannot delete default channel");
1732 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1733 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1734 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1735 mListeners.notifyNotificationChannelChanged(pkg,
1736 UserHandle.getUserHandleForUid(callingUid),
1737 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1738 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1743 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1745 checkCallerIsSystemOrSameApp(pkg);
1746 return new ParceledListSlice<>(new ArrayList(
1747 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1751 public void deleteNotificationChannelGroup(String pkg, String groupId) {
1752 checkCallerIsSystemOrSameApp(pkg);
1754 final int callingUid = Binder.getCallingUid();
1755 NotificationChannelGroup groupToDelete =
1756 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1757 if (groupToDelete != null) {
1758 List<NotificationChannel> deletedChannels =
1759 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1760 for (int i = 0; i < deletedChannels.size(); i++) {
1761 final NotificationChannel deletedChannel = deletedChannels.get(i);
1762 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1764 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1766 mListeners.notifyNotificationChannelChanged(pkg,
1767 UserHandle.getUserHandleForUid(callingUid),
1769 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1771 mListeners.notifyNotificationChannelGroupChanged(
1772 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1773 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1779 public void updateNotificationChannelForPackage(String pkg, int uid,
1780 NotificationChannel channel) {
1781 enforceSystemOrSystemUI("Caller not system or systemui");
1782 Preconditions.checkNotNull(channel);
1783 updateNotificationChannelInt(pkg, uid, channel, false);
1787 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1788 int uid, boolean includeDeleted) {
1789 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1790 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1794 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1795 boolean includeDeleted) {
1796 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1797 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1802 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1803 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1804 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1808 public int getDeletedChannelCount(String pkg, int uid) {
1809 enforceSystemOrSystemUI("getDeletedChannelCount");
1810 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1814 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1815 String pkg, int uid, boolean includeDeleted) {
1816 checkCallerIsSystem();
1817 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1821 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1822 String groupId, String pkg, int uid) {
1823 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1824 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1828 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1829 checkCallerIsSystemOrSameApp(pkg);
1830 return mRankingHelper.getNotificationChannels(
1831 pkg, Binder.getCallingUid(), false /* includeDeleted */);
1835 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1836 checkCallerIsSystem();
1838 // Cancel posted notifications
1839 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1840 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1842 // Listener & assistant
1843 mListeners.onPackagesChanged(true, new String[] {packageName});
1844 mNotificationAssistants.onPackagesChanged(true, new String[] {packageName});
1847 mConditionProviders.onPackagesChanged(true, new String[] {packageName});
1849 // Reset notification preferences
1851 mRankingHelper.onPackagesChanged(true, UserHandle.getCallingUserId(),
1852 new String[]{packageName}, new int[]{uid});
1860 * System-only API for getting a list of current (i.e. not cleared) notifications.
1862 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1863 * @returns A list of all the notifications, in natural order.
1866 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1867 // enforce() will ensure the calling uid has the correct permission
1868 getContext().enforceCallingOrSelfPermission(
1869 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1870 "NotificationManagerService.getActiveNotifications");
1872 StatusBarNotification[] tmp = null;
1873 int uid = Binder.getCallingUid();
1875 // noteOp will check to make sure the callingPkg matches the uid
1876 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1877 == AppOpsManager.MODE_ALLOWED) {
1878 synchronized (mNotificationLock) {
1879 tmp = new StatusBarNotification[mNotificationList.size()];
1880 final int N = mNotificationList.size();
1881 for (int i=0; i<N; i++) {
1882 tmp[i] = mNotificationList.get(i).sbn;
1890 * Public API for getting a list of current notifications for the calling package/uid.
1892 * Note that since notification posting is done asynchronously, this will not return
1893 * notifications that are in the process of being posted.
1895 * @returns A list of all the package's notifications, in natural order.
1898 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1899 int incomingUserId) {
1900 checkCallerIsSystemOrSameApp(pkg);
1901 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1902 Binder.getCallingUid(), incomingUserId, true, false,
1903 "getAppActiveNotifications", pkg);
1904 synchronized (mNotificationLock) {
1905 final ArrayMap<String, StatusBarNotification> map
1906 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
1907 final int N = mNotificationList.size();
1908 for (int i = 0; i < N; i++) {
1909 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1910 mNotificationList.get(i).sbn);
1912 map.put(sbn.getKey(), sbn);
1915 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
1916 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
1918 map.put(sbn.getKey(), sbn);
1921 final int M = mEnqueuedNotifications.size();
1922 for (int i = 0; i < M; i++) {
1923 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1924 mEnqueuedNotifications.get(i).sbn);
1926 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
1929 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
1930 list.addAll(map.values());
1931 return new ParceledListSlice<StatusBarNotification>(list);
1935 private StatusBarNotification sanitizeSbn(String pkg, int userId,
1936 StatusBarNotification sbn) {
1937 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1938 && (sbn.getNotification().flags
1939 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1940 // We could pass back a cloneLight() but clients might get confused and
1941 // try to send this thing back to notify() again, which would not work
1943 return new StatusBarNotification(
1944 sbn.getPackageName(),
1946 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1947 sbn.getNotification().clone(),
1948 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
1954 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1956 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1959 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1960 // enforce() will ensure the calling uid has the correct permission
1961 getContext().enforceCallingOrSelfPermission(
1962 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1963 "NotificationManagerService.getHistoricalNotifications");
1965 StatusBarNotification[] tmp = null;
1966 int uid = Binder.getCallingUid();
1968 // noteOp will check to make sure the callingPkg matches the uid
1969 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1970 == AppOpsManager.MODE_ALLOWED) {
1971 synchronized (mArchive) {
1972 tmp = mArchive.getArray(count);
1979 * Register a listener binder directly with the notification manager.
1981 * Only works with system callers. Apps should extend
1982 * {@link android.service.notification.NotificationListenerService}.
1985 public void registerListener(final INotificationListener listener,
1986 final ComponentName component, final int userid) {
1987 enforceSystemOrSystemUI("INotificationManager.registerListener");
1988 mListeners.registerService(listener, component, userid);
1992 * Remove a listener binder directly
1995 public void unregisterListener(INotificationListener token, int userid) {
1996 mListeners.unregisterService(token, userid);
2000 * Allow an INotificationListener to simulate a "clear all" operation.
2002 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2004 * @param token The binder for the listener, to check that the caller is allowed
2007 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
2008 final int callingUid = Binder.getCallingUid();
2009 final int callingPid = Binder.getCallingPid();
2010 long identity = Binder.clearCallingIdentity();
2012 synchronized (mNotificationLock) {
2013 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2015 final int N = keys.length;
2016 for (int i = 0; i < N; i++) {
2017 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2018 if (r == null) continue;
2019 final int userId = r.sbn.getUserId();
2020 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2021 !mUserProfiles.isCurrentProfile(userId)) {
2022 throw new SecurityException("Disallowed call from listener: "
2025 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2026 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2030 cancelAllLocked(callingUid, callingPid, info.userid,
2031 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
2035 Binder.restoreCallingIdentity(identity);
2040 * Handle request from an approved listener to re-enable itself.
2042 * @param component The componenet to be re-enabled, caller must match package.
2045 public void requestBindListener(ComponentName component) {
2046 checkCallerIsSystemOrSameApp(component.getPackageName());
2047 long identity = Binder.clearCallingIdentity();
2049 ManagedServices manager =
2050 mNotificationAssistants.isComponentEnabledForCurrentProfiles(component)
2051 ? mNotificationAssistants
2053 manager.setComponentState(component, true);
2055 Binder.restoreCallingIdentity(identity);
2060 public void requestUnbindListener(INotificationListener token) {
2061 long identity = Binder.clearCallingIdentity();
2063 // allow bound services to disable themselves
2064 synchronized (mNotificationLock) {
2065 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2066 info.getOwner().setComponentState(info.component, false);
2069 Binder.restoreCallingIdentity(identity);
2074 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2075 long identity = Binder.clearCallingIdentity();
2077 synchronized (mNotificationLock) {
2078 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2080 final int N = keys.length;
2081 for (int i = 0; i < N; i++) {
2082 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2083 if (r == null) continue;
2084 final int userId = r.sbn.getUserId();
2085 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2086 !mUserProfiles.isCurrentProfile(userId)) {
2087 throw new SecurityException("Disallowed call from listener: "
2091 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2092 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2093 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2095 UsageEvents.Event.USER_INTERACTION);
2102 Binder.restoreCallingIdentity(identity);
2107 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2109 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2111 * @param info The binder for the listener, to check that the caller is allowed
2113 @GuardedBy("mNotificationLock")
2114 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2115 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2116 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2117 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2119 userId, REASON_LISTENER_CANCEL, info);
2123 * Allow an INotificationListener to snooze a single notification until a context.
2125 * @param token The binder for the listener, to check that the caller is allowed
2128 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2129 String key, String snoozeCriterionId) {
2130 long identity = Binder.clearCallingIdentity();
2132 synchronized (mNotificationLock) {
2133 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2134 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2137 Binder.restoreCallingIdentity(identity);
2142 * Allow an INotificationListener to snooze a single notification until a time.
2144 * @param token The binder for the listener, to check that the caller is allowed
2147 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2149 long identity = Binder.clearCallingIdentity();
2151 synchronized (mNotificationLock) {
2152 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2153 snoozeNotificationInt(key, duration, null, info);
2156 Binder.restoreCallingIdentity(identity);
2161 * Allows the notification assistant to un-snooze a single notification.
2163 * @param token The binder for the assistant, to check that the caller is allowed
2166 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2167 long identity = Binder.clearCallingIdentity();
2169 synchronized (mNotificationLock) {
2170 final ManagedServiceInfo info =
2171 mNotificationAssistants.checkServiceTokenLocked(token);
2172 unsnoozeNotificationInt(key, info);
2175 Binder.restoreCallingIdentity(identity);
2180 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2182 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2184 * @param token The binder for the listener, to check that the caller is allowed
2187 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2188 String tag, int id) {
2189 final int callingUid = Binder.getCallingUid();
2190 final int callingPid = Binder.getCallingPid();
2191 long identity = Binder.clearCallingIdentity();
2193 synchronized (mNotificationLock) {
2194 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2195 if (info.supportsProfiles()) {
2196 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2197 + "from " + info.component
2198 + " use cancelNotification(key) instead.");
2200 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2201 pkg, tag, id, info.userid);
2205 Binder.restoreCallingIdentity(identity);
2210 * Allow an INotificationListener to request the list of outstanding notifications seen by
2211 * the current user. Useful when starting up, after which point the listener callbacks
2214 * @param token The binder for the listener, to check that the caller is allowed
2215 * @param keys An array of notification keys to fetch, or null to fetch everything
2216 * @returns The return value will contain the notifications specified in keys, in that
2217 * order, or if keys is null, all the notifications, in natural order.
2220 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2221 INotificationListener token, String[] keys, int trim) {
2222 synchronized (mNotificationLock) {
2223 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2224 final boolean getKeys = keys != null;
2225 final int N = getKeys ? keys.length : mNotificationList.size();
2226 final ArrayList<StatusBarNotification> list
2227 = new ArrayList<StatusBarNotification>(N);
2228 for (int i=0; i<N; i++) {
2229 final NotificationRecord r = getKeys
2230 ? mNotificationsByKey.get(keys[i])
2231 : mNotificationList.get(i);
2232 if (r == null) continue;
2233 StatusBarNotification sbn = r.sbn;
2234 if (!isVisibleToListener(sbn, info)) continue;
2235 StatusBarNotification sbnToSend =
2236 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2237 list.add(sbnToSend);
2239 return new ParceledListSlice<StatusBarNotification>(list);
2244 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2245 * seen by the current user. Useful when starting up, after which point the listener
2246 * callbacks should be used.
2248 * @param token The binder for the listener, to check that the caller is allowed
2249 * @returns The return value will contain the notifications specified in keys, in that
2250 * order, or if keys is null, all the notifications, in natural order.
2253 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2254 INotificationListener token, int trim) {
2255 synchronized (mNotificationLock) {
2256 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2257 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2258 final int N = snoozedRecords.size();
2259 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2260 for (int i=0; i < N; i++) {
2261 final NotificationRecord r = snoozedRecords.get(i);
2262 if (r == null) continue;
2263 StatusBarNotification sbn = r.sbn;
2264 if (!isVisibleToListener(sbn, info)) continue;
2265 StatusBarNotification sbnToSend =
2266 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2267 list.add(sbnToSend);
2269 return new ParceledListSlice<>(list);
2274 public void requestHintsFromListener(INotificationListener token, int hints) {
2275 final long identity = Binder.clearCallingIdentity();
2277 synchronized (mNotificationLock) {
2278 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2279 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2280 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2281 | HINT_HOST_DISABLE_CALL_EFFECTS;
2282 final boolean disableEffects = (hints & disableEffectsMask) != 0;
2283 if (disableEffects) {
2284 addDisabledHints(info, hints);
2286 removeDisabledHints(info, hints);
2288 updateListenerHintsLocked();
2289 updateEffectsSuppressorLocked();
2292 Binder.restoreCallingIdentity(identity);
2297 public int getHintsFromListener(INotificationListener token) {
2298 synchronized (mNotificationLock) {
2299 return mListenerHints;
2304 public void requestInterruptionFilterFromListener(INotificationListener token,
2305 int interruptionFilter) throws RemoteException {
2306 final long identity = Binder.clearCallingIdentity();
2308 synchronized (mNotificationLock) {
2309 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2310 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2311 updateInterruptionFilterLocked();
2314 Binder.restoreCallingIdentity(identity);
2319 public int getInterruptionFilterFromListener(INotificationListener token)
2320 throws RemoteException {
2321 synchronized (mNotificationLight) {
2322 return mInterruptionFilter;
2327 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2328 throws RemoteException {
2329 synchronized (mNotificationLock) {
2330 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2331 if (info == null) return;
2332 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2337 public int getZenMode() {
2338 return mZenModeHelper.getZenMode();
2342 public ZenModeConfig getZenModeConfig() {
2343 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2344 return mZenModeHelper.getConfig();
2348 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2349 enforceSystemOrSystemUI("INotificationManager.setZenMode");
2350 final long identity = Binder.clearCallingIdentity();
2352 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2354 Binder.restoreCallingIdentity(identity);
2359 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2360 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2361 return mZenModeHelper.getZenRules();
2365 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2366 Preconditions.checkNotNull(id, "Id is null");
2367 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2368 return mZenModeHelper.getAutomaticZenRule(id);
2372 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2373 throws RemoteException {
2374 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2375 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2376 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2377 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2378 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2380 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2381 "addAutomaticZenRule");
2385 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2386 throws RemoteException {
2387 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2388 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2389 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2390 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2391 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2393 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2394 "updateAutomaticZenRule");
2398 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2399 Preconditions.checkNotNull(id, "Id is null");
2400 // Verify that they can modify zen rules.
2401 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2403 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2407 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2408 Preconditions.checkNotNull(packageName, "Package name is null");
2409 enforceSystemOrSystemUI("removeAutomaticZenRules");
2411 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2415 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2416 Preconditions.checkNotNull(owner, "Owner is null");
2417 enforceSystemOrSystemUI("getRuleInstanceCount");
2419 return mZenModeHelper.getCurrentInstanceCount(owner);
2423 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2424 enforcePolicyAccess(pkg, "setInterruptionFilter");
2425 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2426 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2427 final long identity = Binder.clearCallingIdentity();
2429 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2431 Binder.restoreCallingIdentity(identity);
2436 public void notifyConditions(final String pkg, IConditionProvider provider,
2437 final Condition[] conditions) {
2438 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2439 checkCallerIsSystemOrSameApp(pkg);
2440 mHandler.post(new Runnable() {
2443 mConditionProviders.notifyConditions(pkg, info, conditions);
2449 public void requestUnbindProvider(IConditionProvider provider) {
2450 long identity = Binder.clearCallingIdentity();
2452 // allow bound services to disable themselves
2453 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2454 info.getOwner().setComponentState(info.component, false);
2456 Binder.restoreCallingIdentity(identity);
2461 public void requestBindProvider(ComponentName component) {
2462 checkCallerIsSystemOrSameApp(component.getPackageName());
2463 long identity = Binder.clearCallingIdentity();
2465 mConditionProviders.setComponentState(component, true);
2467 Binder.restoreCallingIdentity(identity);
2471 private void enforceSystemOrSystemUI(String message) {
2472 if (isCallerSystemOrPhone()) return;
2473 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2477 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2479 checkCallerIsSystemOrSameApp(pkg);
2480 } catch (SecurityException e) {
2481 getContext().enforceCallingPermission(
2482 android.Manifest.permission.STATUS_BAR_SERVICE,
2487 private void enforcePolicyAccess(int uid, String method) {
2488 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2489 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2492 boolean accessAllowed = false;
2493 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2494 final int packageCount = packages.length;
2495 for (int i = 0; i < packageCount; i++) {
2496 if (checkPolicyAccess(packages[i])) {
2497 accessAllowed = true;
2500 if (!accessAllowed) {
2501 Slog.w(TAG, "Notification policy access denied calling " + method);
2502 throw new SecurityException("Notification policy access denied");
2506 private void enforcePolicyAccess(String pkg, String method) {
2507 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2508 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2511 checkCallerIsSameApp(pkg);
2512 if (!checkPolicyAccess(pkg)) {
2513 Slog.w(TAG, "Notification policy access denied calling " + method);
2514 throw new SecurityException("Notification policy access denied");
2518 private boolean checkPackagePolicyAccess(String pkg) {
2519 return mPolicyAccess.isPackageGranted(pkg);
2522 private boolean checkPolicyAccess(String pkg) {
2524 int uid = getContext().getPackageManager().getPackageUidAsUser(
2525 pkg, UserHandle.getCallingUserId());
2526 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2527 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2531 } catch (NameNotFoundException e) {
2534 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2538 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2539 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2540 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2541 if (filter != null && filter.stats) {
2542 dumpJson(pw, filter);
2543 } else if (filter != null && filter.proto) {
2544 dumpProto(fd, filter);
2546 dumpImpl(pw, filter);
2551 public ComponentName getEffectsSuppressor() {
2552 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2556 public boolean matchesCallFilter(Bundle extras) {
2557 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2558 return mZenModeHelper.matchesCallFilter(
2559 Binder.getCallingUserHandle(),
2561 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2562 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2563 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2567 public boolean isSystemConditionProviderEnabled(String path) {
2568 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2569 return mConditionProviders.isSystemProviderEnabled(path);
2572 // Backup/restore interface
2574 public byte[] getBackupPayload(int user) {
2575 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2576 //TODO: http://b/22388012
2577 if (user != UserHandle.USER_SYSTEM) {
2578 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2581 synchronized(mPolicyFile) {
2582 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2584 writePolicyXml(baos, true /*forBackup*/);
2585 return baos.toByteArray();
2586 } catch (IOException e) {
2587 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2594 public void applyRestore(byte[] payload, int user) {
2595 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2596 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2597 if (payload == null) {
2598 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2601 //TODO: http://b/22388012
2602 if (user != UserHandle.USER_SYSTEM) {
2603 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2606 synchronized(mPolicyFile) {
2607 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2609 readPolicyXml(bais, true /*forRestore*/);
2611 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2612 Slog.w(TAG, "applyRestore: error reading payload", e);
2618 public boolean isNotificationPolicyAccessGranted(String pkg) {
2619 return checkPolicyAccess(pkg);
2623 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2624 enforceSystemOrSystemUIOrSamePackage(pkg,
2625 "request policy access status for another package");
2626 return checkPolicyAccess(pkg);
2630 public String[] getPackagesRequestingNotificationPolicyAccess()
2631 throws RemoteException {
2632 enforceSystemOrSystemUI("request policy access packages");
2633 final long identity = Binder.clearCallingIdentity();
2635 return mPolicyAccess.getRequestingPackages();
2637 Binder.restoreCallingIdentity(identity);
2642 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2643 throws RemoteException {
2644 enforceSystemOrSystemUI("grant notification policy access");
2645 final long identity = Binder.clearCallingIdentity();
2647 synchronized (mNotificationLock) {
2648 mPolicyAccess.put(pkg, granted);
2651 Binder.restoreCallingIdentity(identity);
2656 public Policy getNotificationPolicy(String pkg) {
2657 enforcePolicyAccess(pkg, "getNotificationPolicy");
2658 final long identity = Binder.clearCallingIdentity();
2660 return mZenModeHelper.getNotificationPolicy();
2662 Binder.restoreCallingIdentity(identity);
2667 public void setNotificationPolicy(String pkg, Policy policy) {
2668 enforcePolicyAccess(pkg, "setNotificationPolicy");
2669 final long identity = Binder.clearCallingIdentity();
2671 mZenModeHelper.setNotificationPolicy(policy);
2673 Binder.restoreCallingIdentity(identity);
2678 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2679 Adjustment adjustment) throws RemoteException {
2680 final long identity = Binder.clearCallingIdentity();
2682 synchronized (mNotificationLock) {
2683 mNotificationAssistants.checkServiceTokenLocked(token);
2684 int N = mEnqueuedNotifications.size();
2685 for (int i = 0; i < N; i++) {
2686 final NotificationRecord n = mEnqueuedNotifications.get(i);
2687 if (Objects.equals(adjustment.getKey(), n.getKey())
2688 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2689 applyAdjustment(n, adjustment);
2695 Binder.restoreCallingIdentity(identity);
2700 public void applyAdjustmentFromAssistant(INotificationListener token,
2701 Adjustment adjustment) throws RemoteException {
2702 final long identity = Binder.clearCallingIdentity();
2704 synchronized (mNotificationLock) {
2705 mNotificationAssistants.checkServiceTokenLocked(token);
2706 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2707 applyAdjustment(n, adjustment);
2709 mRankingHandler.requestSort(true);
2711 Binder.restoreCallingIdentity(identity);
2716 public void applyAdjustmentsFromAssistant(INotificationListener token,
2717 List<Adjustment> adjustments) throws RemoteException {
2719 final long identity = Binder.clearCallingIdentity();
2721 synchronized (mNotificationLock) {
2722 mNotificationAssistants.checkServiceTokenLocked(token);
2723 for (Adjustment adjustment : adjustments) {
2724 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2725 applyAdjustment(n, adjustment);
2728 mRankingHandler.requestSort(true);
2730 Binder.restoreCallingIdentity(identity);
2735 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2736 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2737 Preconditions.checkNotNull(channel);
2738 Preconditions.checkNotNull(pkg);
2739 Preconditions.checkNotNull(user);
2741 verifyPrivilegedListener(token, user);
2742 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2746 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2747 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2748 Preconditions.checkNotNull(pkg);
2749 Preconditions.checkNotNull(user);
2750 verifyPrivilegedListener(token, user);
2752 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2753 false /* includeDeleted */);
2757 public ParceledListSlice<NotificationChannelGroup>
2758 getNotificationChannelGroupsFromPrivilegedListener(
2759 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2760 Preconditions.checkNotNull(pkg);
2761 Preconditions.checkNotNull(user);
2762 verifyPrivilegedListener(token, user);
2764 List<NotificationChannelGroup> groups = new ArrayList<>();
2765 groups.addAll(mRankingHelper.getNotificationChannelGroups(
2766 pkg, getUidForPackageAndUser(pkg, user)));
2767 return new ParceledListSlice<>(groups);
2770 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
2771 ManagedServiceInfo info;
2772 synchronized (mNotificationLock) {
2773 info = mListeners.checkServiceTokenLocked(token);
2775 if (!hasCompanionDevice(info)) {
2776 throw new SecurityException(info + " does not have access");
2778 if (!info.enabledAndUserMatches(user.getIdentifier())) {
2779 throw new SecurityException(info + " does not have access");
2783 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2785 long identity = Binder.clearCallingIdentity();
2787 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2789 Binder.restoreCallingIdentity(identity);
2795 private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
2799 if (adjustment.getSignals() != null) {
2800 Bundle.setDefusable(adjustment.getSignals(), true);
2801 final ArrayList<String> people =
2802 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
2803 final ArrayList<SnoozeCriterion> snoozeCriterionList =
2804 adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
2805 n.setPeopleOverride(people);
2806 n.setSnoozeCriteria(snoozeCriterionList);
2810 @GuardedBy("mNotificationLock")
2811 private void addAutogroupKeyLocked(String key) {
2812 NotificationRecord n = mNotificationsByKey.get(key);
2816 n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
2817 EventLogTags.writeNotificationAutogrouped(key);
2820 @GuardedBy("mNotificationLock")
2821 private void removeAutogroupKeyLocked(String key) {
2822 NotificationRecord n = mNotificationsByKey.get(key);
2826 n.setOverrideGroupKey(null);
2827 EventLogTags.writeNotificationUnautogrouped(key);
2830 // Clears the 'fake' auto-group summary.
2831 @GuardedBy("mNotificationLock")
2832 private void clearAutogroupSummaryLocked(int userId, String pkg) {
2833 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2834 if (summaries != null && summaries.containsKey(pkg)) {
2836 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
2837 if (removed != null) {
2838 boolean wasPosted = removeFromNotificationListsLocked(removed);
2839 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
2844 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2845 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
2846 NotificationRecord summaryRecord = null;
2847 synchronized (mNotificationLock) {
2848 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
2849 if (notificationRecord == null) {
2850 // The notification could have been cancelled again already. A successive
2851 // adjustment will post a summary if needed.
2854 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2855 userId = adjustedSbn.getUser().getIdentifier();
2856 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2857 if (summaries == null) {
2858 summaries = new ArrayMap<>();
2860 mAutobundledSummaries.put(userId, summaries);
2861 if (!summaries.containsKey(pkg)) {
2863 final ApplicationInfo appInfo =
2864 adjustedSbn.getNotification().extras.getParcelable(
2865 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2866 final Bundle extras = new Bundle();
2867 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2868 final String channelId = notificationRecord.getChannel().getId();
2869 final Notification summaryNotification =
2870 new Notification.Builder(getContext(), channelId)
2871 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
2872 .setGroupSummary(true)
2873 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
2874 .setGroup(GroupHelper.AUTOGROUP_KEY)
2875 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2876 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2877 .setColor(adjustedSbn.getNotification().color)
2880 summaryNotification.extras.putAll(extras);
2881 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
2882 if (appIntent != null) {
2883 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2884 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
2886 final StatusBarNotification summarySbn =
2887 new StatusBarNotification(adjustedSbn.getPackageName(),
2888 adjustedSbn.getOpPkg(),
2890 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
2891 adjustedSbn.getInitialPid(), summaryNotification,
2892 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
2893 System.currentTimeMillis());
2894 summaryRecord = new NotificationRecord(getContext(), summarySbn,
2895 notificationRecord.getChannel());
2896 summaries.put(pkg, summarySbn.getKey());
2899 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
2900 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
2901 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2905 private String disableNotificationEffects(NotificationRecord record) {
2906 if (mDisableNotificationEffects) {
2907 return "booleanState";
2909 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2910 return "listenerHints";
2912 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2918 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2919 JSONObject dump = new JSONObject();
2921 dump.put("service", "Notification Manager");
2922 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2923 dump.put("ranking", mRankingHelper.dumpJson(filter));
2924 dump.put("stats", mUsageStats.dumpJson(filter));
2925 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
2926 } catch (JSONException e) {
2927 e.printStackTrace();
2932 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
2933 final ProtoOutputStream proto = new ProtoOutputStream(fd);
2934 synchronized (mNotificationLock) {
2935 long records = proto.start(NotificationServiceDumpProto.RECORDS);
2936 int N = mNotificationList.size();
2938 for (int i = 0; i < N; i++) {
2939 final NotificationRecord nr = mNotificationList.get(i);
2940 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2941 nr.dump(proto, filter.redact);
2942 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
2945 N = mEnqueuedNotifications.size();
2947 for (int i = 0; i < N; i++) {
2948 final NotificationRecord nr = mEnqueuedNotifications.get(i);
2949 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2950 nr.dump(proto, filter.redact);
2951 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
2954 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
2957 for (int i = 0; i < N; i++) {
2958 final NotificationRecord nr = snoozed.get(i);
2959 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2960 nr.dump(proto, filter.redact);
2961 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
2967 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
2968 mZenModeHelper.dump(proto);
2969 for (ComponentName suppressor : mEffectsSuppressors) {
2970 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
2977 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2978 pw.print("Current Notification Manager state");
2979 if (filter.filtered) {
2980 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2984 final boolean zenOnly = filter.filtered && filter.zen;
2987 synchronized (mToastQueue) {
2988 N = mToastQueue.size();
2990 pw.println(" Toast Queue:");
2991 for (int i=0; i<N; i++) {
2992 mToastQueue.get(i).dump(pw, " ", filter);
2999 synchronized (mNotificationLock) {
3001 N = mNotificationList.size();
3003 pw.println(" Notification List:");
3004 for (int i=0; i<N; i++) {
3005 final NotificationRecord nr = mNotificationList.get(i);
3006 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3007 nr.dump(pw, " ", getContext(), filter.redact);
3012 if (!filter.filtered) {
3015 pw.println(" Lights List:");
3016 for (int i=0; i<N; i++) {
3022 pw.println(mLights.get(i));
3026 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3027 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
3028 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3029 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
3030 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
3031 pw.println(" mCallState=" + callStateToString(mCallState));
3032 pw.println(" mSystemReady=" + mSystemReady);
3033 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
3035 pw.println(" mArchive=" + mArchive.toString());
3036 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
3038 while (iter.hasNext()) {
3039 final StatusBarNotification sbn = iter.next();
3040 if (filter != null && !filter.matches(sbn)) continue;
3041 pw.println(" " + sbn);
3043 if (iter.hasNext()) pw.println(" ...");
3049 N = mEnqueuedNotifications.size();
3051 pw.println(" Enqueued Notification List:");
3052 for (int i = 0; i < N; i++) {
3053 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3054 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3055 nr.dump(pw, " ", getContext(), filter.redact);
3060 mSnoozeHelper.dump(pw, filter);
3065 pw.println("\n Ranking Config:");
3066 mRankingHelper.dump(pw, " ", filter);
3068 pw.println("\n Notification listeners:");
3069 mListeners.dump(pw, filter);
3070 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3071 pw.print(" mListenersDisablingEffects: (");
3072 N = mListenersDisablingEffects.size();
3073 for (int i = 0; i < N; i++) {
3074 final int hint = mListenersDisablingEffects.keyAt(i);
3075 if (i > 0) pw.print(';');
3076 pw.print("hint[" + hint + "]:");
3078 final ArraySet<ManagedServiceInfo> listeners =
3079 mListenersDisablingEffects.valueAt(i);
3080 final int listenerSize = listeners.size();
3082 for (int j = 0; j < listenerSize; j++) {
3083 if (i > 0) pw.print(',');
3084 final ManagedServiceInfo listener = listeners.valueAt(i);
3085 pw.print(listener.component);
3089 pw.println("\n Notification assistant services:");
3090 mNotificationAssistants.dump(pw, filter);
3093 if (!filter.filtered || zenOnly) {
3094 pw.println("\n Zen Mode:");
3095 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3096 mZenModeHelper.dump(pw, " ");
3098 pw.println("\n Zen Log:");
3099 ZenLog.dump(pw, " ");
3102 pw.println("\n Policy access:");
3103 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
3105 pw.println("\n Condition providers:");
3106 mConditionProviders.dump(pw, filter);
3108 pw.println("\n Group summaries:");
3109 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3110 NotificationRecord r = entry.getValue();
3111 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3112 if (mNotificationsByKey.get(r.getKey()) != r) {
3113 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3114 r.dump(pw, " ", getContext(), filter.redact);
3119 pw.println("\n Usage Stats:");
3120 mUsageStats.dump(pw, " ", filter);
3126 * The private API only accessible to the system process.
3128 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3130 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3131 String tag, int id, Notification notification, int userId) {
3132 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3137 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3139 checkCallerIsSystem();
3140 mHandler.post(new Runnable() {
3143 synchronized (mNotificationLock) {
3144 removeForegroundServiceFlagByListLocked(
3145 mEnqueuedNotifications, pkg, notificationId, userId);
3146 removeForegroundServiceFlagByListLocked(
3147 mNotificationList, pkg, notificationId, userId);
3153 @GuardedBy("mNotificationLock")
3154 private void removeForegroundServiceFlagByListLocked(
3155 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3157 NotificationRecord r = findNotificationByListLocked(
3158 notificationList, pkg, null, notificationId, userId);
3162 StatusBarNotification sbn = r.sbn;
3163 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3164 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3165 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3166 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3167 sbn.getNotification().flags =
3168 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3169 mRankingHelper.sort(mNotificationList);
3170 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3171 mGroupHelper.onNotificationPosted(sbn);
3175 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3176 final int callingPid, final String tag, final int id, final Notification notification,
3177 int incomingUserId) {
3179 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3180 + " notification=" + notification);
3182 checkCallerIsSystemOrSameApp(pkg);
3184 final int userId = ActivityManager.handleIncomingUser(callingPid,
3185 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3186 final UserHandle user = new UserHandle(userId);
3188 if (pkg == null || notification == null) {
3189 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3190 + " id=" + id + " notification=" + notification);
3193 // The system can post notifications for any package, let us resolve that.
3194 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3196 // Fix the notification as best we can.
3198 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3199 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3200 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3201 Notification.addFieldsFromContext(ai, notification);
3202 } catch (NameNotFoundException e) {
3203 Slog.e(TAG, "Cannot create a context for sending app", e);
3207 mUsageStats.registerEnqueuedByApp(pkg);
3209 // setup local book-keeping
3210 String channelId = notification.getChannelId();
3211 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3212 channelId = (new Notification.TvExtender(notification)).getChannelId();
3214 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3215 notificationUid, channelId, false /* includeDeleted */);
3216 if (channel == null) {
3217 final String noChannelStr = "No Channel found for "
3219 + ", channelId=" + channelId
3222 + ", opPkg=" + opPkg
3223 + ", callingUid=" + callingUid
3224 + ", userId=" + userId
3225 + ", incomingUserId=" + incomingUserId
3226 + ", notificationUid=" + notificationUid
3227 + ", notification=" + notification;
3228 Log.e(TAG, noChannelStr);
3229 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3230 "Failed to post notification on channel \"" + channelId + "\"\n" +
3231 "See log for more details");
3235 final StatusBarNotification n = new StatusBarNotification(
3236 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3237 user, null, System.currentTimeMillis());
3238 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3240 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
3244 // Whitelist pending intents.
3245 if (notification.allPendingIntents != null) {
3246 final int intentCount = notification.allPendingIntents.size();
3247 if (intentCount > 0) {
3248 final ActivityManagerInternal am = LocalServices
3249 .getService(ActivityManagerInternal.class);
3250 final long duration = LocalServices.getService(
3251 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3252 for (int i = 0; i < intentCount; i++) {
3253 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3254 if (pendingIntent != null) {
3255 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3256 WHITELIST_TOKEN, duration);
3262 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3265 private void doChannelWarningToast(CharSequence toastText) {
3266 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3267 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3268 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3269 if (warningEnabled) {
3270 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3271 Toast.LENGTH_SHORT);
3276 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3277 // The system can post notifications on behalf of any package it wants
3278 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3280 return getContext().getPackageManager()
3281 .getPackageUidAsUser(opPackageName, userId);
3282 } catch (NameNotFoundException e) {
3290 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3294 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3295 NotificationRecord r) {
3296 final String pkg = r.sbn.getPackageName();
3297 final boolean isSystemNotification =
3298 isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
3299 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3301 // Limit the number of notifications that any given package except the android
3302 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3303 if (!isSystemNotification && !isNotificationFromListener) {
3304 synchronized (mNotificationLock) {
3305 if (mNotificationsByKey.get(r.sbn.getKey()) != null) {
3306 // this is an update, rate limit updates only
3307 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3308 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3309 mUsageStats.registerOverRateQuota(pkg);
3310 final long now = SystemClock.elapsedRealtime();
3311 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3312 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3313 + ". Shedding events. package=" + pkg);
3314 mLastOverRateLogTime = now;
3318 } else if (isCallerInstantApp(pkg)) {
3319 // Ephemeral apps have some special constraints for notifications.
3320 // They are not allowed to create new notifications however they are allowed to
3321 // update notifications created by the system (e.g. a foreground service
3323 throw new SecurityException("Instant app " + pkg
3324 + " cannot create notifications");
3328 final int N = mNotificationList.size();
3329 for (int i=0; i<N; i++) {
3330 final NotificationRecord existing = mNotificationList.get(i);
3331 if (existing.sbn.getPackageName().equals(pkg)
3332 && existing.sbn.getUserId() == userId) {
3333 if (existing.sbn.getId() == id
3334 && TextUtils.equals(existing.sbn.getTag(), tag)) {
3335 break; // Allow updating existing notification
3338 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3339 mUsageStats.registerOverCountQuota(pkg);
3340 Slog.e(TAG, "Package has already posted " + count
3341 + " notifications. Not showing more. package=" + pkg);
3350 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3351 MetricsLogger.action(r.getLogMaker()
3352 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3353 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3355 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3357 mSnoozeHelper.update(userId, r);
3364 if (isBlocked(r, mUsageStats)) {
3371 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3372 final String pkg = r.sbn.getPackageName();
3373 final int callingUid = r.sbn.getUid();
3375 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3376 if (isPackageSuspended) {
3377 Slog.e(TAG, "Suppressing notification from package due to package "
3378 + "suspended by administrator.");
3379 usageStats.registerSuspendedByAdmin(r);
3380 return isPackageSuspended;
3383 final boolean isBlocked =
3384 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
3385 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3387 Slog.e(TAG, "Suppressing notification from package by user request.");
3388 usageStats.registerBlocked(r);
3393 protected class SnoozeNotificationRunnable implements Runnable {
3394 private final String mKey;
3395 private final long mDuration;
3396 private final String mSnoozeCriterionId;
3398 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3400 mDuration = duration;
3401 mSnoozeCriterionId = snoozeCriterionId;
3406 synchronized (mNotificationLock) {
3407 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3414 @GuardedBy("mNotificationLock")
3415 void snoozeLocked(NotificationRecord r) {
3416 if (r.sbn.isGroup()) {
3417 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3418 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3419 if (r.getNotification().isGroupSummary()) {
3420 // snooze summary and all children
3421 for (int i = 0; i < groupNotifications.size(); i++) {
3422 snoozeNotificationLocked(groupNotifications.get(i));
3425 // if there is a valid summary for this group, and we are snoozing the only
3426 // child, also snooze the summary
3427 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3428 if (groupNotifications.size() != 2) {
3429 snoozeNotificationLocked(r);
3431 // snooze summary and the one child
3432 for (int i = 0; i < groupNotifications.size(); i++) {
3433 snoozeNotificationLocked(groupNotifications.get(i));
3437 snoozeNotificationLocked(r);
3441 // just snooze the one notification
3442 snoozeNotificationLocked(r);
3446 @GuardedBy("mNotificationLock")
3447 void snoozeNotificationLocked(NotificationRecord r) {
3448 MetricsLogger.action(r.getLogMaker()
3449 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3450 .setType(MetricsEvent.TYPE_CLOSE)
3451 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3452 mSnoozeCriterionId == null ? 0 : 1));
3453 boolean wasPosted = removeFromNotificationListsLocked(r);
3454 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
3455 updateLightsLocked();
3456 if (mSnoozeCriterionId != null) {
3457 mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3458 mSnoozeHelper.snooze(r);
3460 mSnoozeHelper.snooze(r, mDuration);
3466 protected class EnqueueNotificationRunnable implements Runnable {
3467 private final NotificationRecord r;
3468 private final int userId;
3470 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3471 this.userId = userId;
3477 synchronized (mNotificationLock) {
3478 mEnqueuedNotifications.add(r);
3479 scheduleTimeoutLocked(r);
3481 final StatusBarNotification n = r.sbn;
3482 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3483 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3485 // Retain ranking information from previous record
3486 r.copyRankingInformation(old);
3489 final int callingUid = n.getUid();
3490 final int callingPid = n.getInitialPid();
3491 final Notification notification = n.getNotification();
3492 final String pkg = n.getPackageName();
3493 final int id = n.getId();
3494 final String tag = n.getTag();
3496 // Handle grouped notifications and bail out early if we
3497 // can to avoid extracting signals.
3498 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3500 // if this is a group child, unsnooze parent summary
3501 if (n.isGroup() && notification.isGroupChild()) {
3502 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3505 // This conditional is a dirty hack to limit the logging done on
3506 // behalf of the download manager without affecting other apps.
3507 if (!pkg.equals("com.android.providers.downloads")
3508 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3509 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3511 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3513 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3514 pkg, id, tag, userId, notification.toString(),
3518 mRankingHelper.extractSignals(r);
3520 // tell the assistant service about the notification
3521 if (mNotificationAssistants.isEnabled()) {
3522 mNotificationAssistants.onNotificationEnqueued(r);
3523 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3524 DELAY_FOR_ASSISTANT_TIME);
3526 mHandler.post(new PostNotificationRunnable(r.getKey()));
3532 protected class PostNotificationRunnable implements Runnable {
3533 private final String key;
3535 PostNotificationRunnable(String key) {
3541 synchronized (mNotificationLock) {
3543 NotificationRecord r = null;
3544 int N = mEnqueuedNotifications.size();
3545 for (int i = 0; i < N; i++) {
3546 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3547 if (Objects.equals(key, enqueued.getKey())) {
3553 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3556 NotificationRecord old = mNotificationsByKey.get(key);
3557 final StatusBarNotification n = r.sbn;
3558 final Notification notification = n.getNotification();
3559 int index = indexOfNotificationLocked(n.getKey());
3561 mNotificationList.add(r);
3562 mUsageStats.registerPostedByApp(r);
3564 old = mNotificationList.get(index);
3565 mNotificationList.set(index, r);
3566 mUsageStats.registerUpdatedByApp(r, old);
3567 // Make sure we don't lose the foreground service state.
3568 notification.flags |=
3569 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3573 mNotificationsByKey.put(n.getKey(), r);
3575 // Ensure if this is a foreground service that the proper additional
3577 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3578 notification.flags |= Notification.FLAG_ONGOING_EVENT
3579 | Notification.FLAG_NO_CLEAR;
3582 applyZenModeLocked(r);
3583 mRankingHelper.sort(mNotificationList);
3585 if (notification.getSmallIcon() != null) {
3586 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3587 mListeners.notifyPostedLocked(n, oldSbn);
3588 mHandler.post(new Runnable() {
3591 mGroupHelper.onNotificationPosted(n);
3595 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3596 if (old != null && !old.isCanceled) {
3597 mListeners.notifyRemovedLocked(n,
3598 NotificationListenerService.REASON_ERROR);
3599 mHandler.post(new Runnable() {
3602 mGroupHelper.onNotificationRemoved(n);
3606 // ATTENTION: in a future release we will bail out here
3607 // so that we do not play sounds, show lights, etc. for invalid
3609 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3610 + n.getPackageName());
3613 buzzBeepBlinkLocked(r);
3615 int N = mEnqueuedNotifications.size();
3616 for (int i = 0; i < N; i++) {
3617 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3618 if (Objects.equals(key, enqueued.getKey())) {
3619 mEnqueuedNotifications.remove(i);
3629 * Ensures that grouped notification receive their special treatment.
3631 * <p>Cancels group children if the new notification causes a group to lose
3634 * <p>Updates mSummaryByGroupKey.</p>
3636 @GuardedBy("mNotificationLock")
3637 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3638 int callingUid, int callingPid) {
3639 StatusBarNotification sbn = r.sbn;
3640 Notification n = sbn.getNotification();
3641 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3642 // notifications without a group shouldn't be a summary, otherwise autobundling can
3644 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3647 String group = sbn.getGroupKey();
3648 boolean isSummary = n.isGroupSummary();
3650 Notification oldN = old != null ? old.sbn.getNotification() : null;
3651 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3652 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3655 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3656 if (removedSummary != old) {
3658 removedSummary != null ? removedSummary.getKey() : "<null>";
3659 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3660 ", removed=" + removedKey);
3664 mSummaryByGroupKey.put(group, r);
3667 // Clear out group children of the old notification if the update
3668 // causes the group summary to go away. This happens when the old
3669 // notification was a summary and the new one isn't, or when the old
3670 // notification was a summary and its group key changed.
3671 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3672 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
3677 @GuardedBy("mNotificationLock")
3678 void scheduleTimeoutLocked(NotificationRecord record) {
3679 if (record.getNotification().getTimeoutAfter() > 0) {
3680 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3681 REQUEST_CODE_TIMEOUT,
3682 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3683 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3684 .appendPath(record.getKey()).build())
3685 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3686 .putExtra(EXTRA_KEY, record.getKey()),
3687 PendingIntent.FLAG_UPDATE_CURRENT);
3688 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3689 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
3694 @GuardedBy("mNotificationLock")
3695 void buzzBeepBlinkLocked(NotificationRecord record) {
3696 boolean buzz = false;
3697 boolean beep = false;
3698 boolean blink = false;
3700 final Notification notification = record.sbn.getNotification();
3701 final String key = record.getKey();
3703 // Should this notification make noise, vibe, or use the LED?
3704 final boolean aboveThreshold =
3705 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
3706 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
3709 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
3710 " intercept=" + record.isIntercepted()
3713 // If we're not supposed to beep, vibrate, etc. then don't.
3714 final String disableEffects = disableNotificationEffects(record);
3715 if (disableEffects != null) {
3716 ZenLog.traceDisableEffects(record, disableEffects);
3719 // Remember if this notification already owns the notification channels.
3720 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3721 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
3722 // These are set inside the conditional if the notification is allowed to make noise.
3723 boolean hasValidVibrate = false;
3724 boolean hasValidSound = false;
3726 if (isNotificationForCurrentUser(record)) {
3727 // If the notification will appear in the status bar, it should send an accessibility
3729 if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
3730 sendAccessibilityEvent(notification, record.sbn.getPackageName());
3733 if (disableEffects == null
3736 && mAudioManager != null) {
3737 if (DBG) Slog.v(TAG, "Interrupting!");
3738 Uri soundUri = record.getSound();
3739 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
3740 long[] vibration = record.getVibration();
3741 // Demote sound to vibration if vibration missing & phone in vibration mode.
3742 if (vibration == null
3744 && (mAudioManager.getRingerModeInternal()
3745 == AudioManager.RINGER_MODE_VIBRATE)) {
3746 vibration = mFallbackVibrationPattern;
3748 hasValidVibrate = vibration != null;
3750 if (!shouldMuteNotificationLocked(record)) {
3751 if (hasValidSound) {
3752 mSoundNotificationKey = key;
3754 playInCallNotification();
3757 beep = playSound(record, soundUri);
3761 final boolean ringerModeSilent =
3762 mAudioManager.getRingerModeInternal()
3763 == AudioManager.RINGER_MODE_SILENT;
3764 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
3765 mVibrateNotificationKey = key;
3767 buzz = playVibration(record, vibration, hasValidSound);
3772 // If a notification is updated to remove the actively playing sound or vibrate,
3773 // cancel that feedback now
3774 if (wasBeep && !hasValidSound) {
3777 if (wasBuzz && !hasValidVibrate) {
3778 clearVibrateLocked();
3782 // release the light
3783 boolean wasShowLights = mLights.remove(key);
3784 if (record.getLight() != null && aboveThreshold
3785 && ((record.getSuppressedVisualEffects()
3786 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
3788 updateLightsLocked();
3789 if (mUseAttentionLight) {
3790 mAttentionLight.pulse();
3793 } else if (wasShowLights) {
3794 updateLightsLocked();
3796 if (buzz || beep || blink) {
3797 MetricsLogger.action(record.getLogMaker()
3798 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
3799 .setType(MetricsEvent.TYPE_OPEN)
3800 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
3801 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
3805 @GuardedBy("mNotificationLock")
3806 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
3807 final Notification notification = record.getNotification();
3809 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
3812 if (record.sbn.isGroup()) {
3813 return notification.suppressAlertingDueToGrouping();
3818 private boolean playSound(final NotificationRecord record, Uri soundUri) {
3819 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3820 // do not play notifications if there is a user of exclusive audio focus
3821 // or the device is in vibrate mode
3822 if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
3823 != AudioManager.RINGER_MODE_VIBRATE) {
3824 final long identity = Binder.clearCallingIdentity();
3826 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3827 if (player != null) {
3828 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3829 + " with attributes " + record.getAudioAttributes());
3830 player.playAsync(soundUri, record.sbn.getUser(), looping,
3831 record.getAudioAttributes());
3834 } catch (RemoteException e) {
3836 Binder.restoreCallingIdentity(identity);
3842 private boolean playVibration(final NotificationRecord record, long[] vibration,
3843 boolean delayVibForSound) {
3844 // Escalate privileges so we can use the vibrator even if the
3845 // notifying app does not have the VIBRATE permission.
3846 long identity = Binder.clearCallingIdentity();
3848 final VibrationEffect effect;
3850 final boolean insistent =
3851 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3852 effect = VibrationEffect.createWaveform(
3853 vibration, insistent ? 0 : -1 /*repeatIndex*/);
3854 } catch (IllegalArgumentException e) {
3855 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
3856 Arrays.toString(vibration));
3859 if (delayVibForSound) {
3861 // delay the vibration by the same amount as the notification sound
3862 final int waitMs = mAudioManager.getFocusRampTimeMs(
3863 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
3864 record.getAudioAttributes());
3865 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
3867 Thread.sleep(waitMs);
3868 } catch (InterruptedException e) { }
3869 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3870 effect, record.getAudioAttributes());
3873 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3874 effect, record.getAudioAttributes());
3878 Binder.restoreCallingIdentity(identity);
3882 private boolean isNotificationForCurrentUser(NotificationRecord record) {
3883 final int currentUser;
3884 final long token = Binder.clearCallingIdentity();
3886 currentUser = ActivityManager.getCurrentUser();
3888 Binder.restoreCallingIdentity(token);
3890 return (record.getUserId() == UserHandle.USER_ALL ||
3891 record.getUserId() == currentUser ||
3892 mUserProfiles.isCurrentProfile(record.getUserId()));
3895 private void playInCallNotification() {
3899 // If toneGenerator creation fails, just continue the call
3900 // without playing the notification sound.
3902 synchronized (mInCallToneGeneratorLock) {
3903 if (mInCallToneGenerator != null) {
3904 // limit this tone to 1 second; BEEP2 should in fact be much shorter
3905 mInCallToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2, 1000);
3908 } catch (RuntimeException e) {
3909 Log.w(TAG, "Exception from ToneGenerator: " + e);
3915 @GuardedBy("mToastQueue")
3916 void showNextToastLocked() {
3917 ToastRecord record = mToastQueue.get(0);
3918 while (record != null) {
3919 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3921 record.callback.show(record.token);
3922 scheduleTimeoutLocked(record);
3924 } catch (RemoteException e) {
3925 Slog.w(TAG, "Object died trying to show notification " + record.callback
3926 + " in package " + record.pkg);
3927 // remove it from the list and let the process die
3928 int index = mToastQueue.indexOf(record);
3930 mToastQueue.remove(index);
3932 keepProcessAliveIfNeededLocked(record.pid);
3933 if (mToastQueue.size() > 0) {
3934 record = mToastQueue.get(0);
3942 @GuardedBy("mToastQueue")
3943 void cancelToastLocked(int index) {
3944 ToastRecord record = mToastQueue.get(index);
3946 record.callback.hide();
3947 } catch (RemoteException e) {
3948 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3949 + " in package " + record.pkg);
3950 // don't worry about this, we're about to remove it from
3954 ToastRecord lastToast = mToastQueue.remove(index);
3955 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
3957 keepProcessAliveIfNeededLocked(record.pid);
3958 if (mToastQueue.size() > 0) {
3959 // Show the next one. If the callback fails, this will remove
3960 // it from the list, so don't assume that the list hasn't changed
3961 // after this point.
3962 showNextToastLocked();
3966 @GuardedBy("mToastQueue")
3967 private void scheduleTimeoutLocked(ToastRecord r)
3969 mHandler.removeCallbacksAndMessages(r);
3970 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3971 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3972 mHandler.sendMessageDelayed(m, delay);
3975 private void handleTimeout(ToastRecord record)
3977 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3978 synchronized (mToastQueue) {
3979 int index = indexOfToastLocked(record.pkg, record.callback);
3981 cancelToastLocked(index);
3986 @GuardedBy("mToastQueue")
3987 int indexOfToastLocked(String pkg, ITransientNotification callback)
3989 IBinder cbak = callback.asBinder();
3990 ArrayList<ToastRecord> list = mToastQueue;
3991 int len = list.size();
3992 for (int i=0; i<len; i++) {
3993 ToastRecord r = list.get(i);
3994 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
4001 @GuardedBy("mToastQueue")
4002 void keepProcessAliveIfNeededLocked(int pid)
4004 int toastCount = 0; // toasts from this pid
4005 ArrayList<ToastRecord> list = mToastQueue;
4006 int N = list.size();
4007 for (int i=0; i<N; i++) {
4008 ToastRecord r = list.get(i);
4014 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
4015 } catch (RemoteException e) {
4016 // Shouldn't happen.
4020 private void handleRankingReconsideration(Message message) {
4021 if (!(message.obj instanceof RankingReconsideration)) return;
4022 RankingReconsideration recon = (RankingReconsideration) message.obj;
4025 synchronized (mNotificationLock) {
4026 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4027 if (record == null) {
4030 int indexBefore = findNotificationRecordIndexLocked(record);
4031 boolean interceptBefore = record.isIntercepted();
4032 int visibilityBefore = record.getPackageVisibilityOverride();
4033 recon.applyChangesLocked(record);
4034 applyZenModeLocked(record);
4035 mRankingHelper.sort(mNotificationList);
4036 int indexAfter = findNotificationRecordIndexLocked(record);
4037 boolean interceptAfter = record.isIntercepted();
4038 int visibilityAfter = record.getPackageVisibilityOverride();
4039 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4040 || visibilityBefore != visibilityAfter;
4041 if (interceptBefore && !interceptAfter) {
4042 buzzBeepBlinkLocked(record);
4046 scheduleSendRankingUpdate();
4050 private void handleRankingSort(Message msg) {
4051 if (!(msg.obj instanceof Boolean)) return;
4052 if (mRankingHelper == null) return;
4053 boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
4054 synchronized (mNotificationLock) {
4055 final int N = mNotificationList.size();
4056 // Any field that can change via one of the extractors or by the assistant
4057 // needs to be added here.
4058 ArrayList<String> orderBefore = new ArrayList<String>(N);
4059 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
4060 int[] visibilities = new int[N];
4061 boolean[] showBadges = new boolean[N];
4062 for (int i = 0; i < N; i++) {
4063 final NotificationRecord r = mNotificationList.get(i);
4064 orderBefore.add(r.getKey());
4065 groupOverrideBefore.add(r.sbn.getGroupKey());
4066 visibilities[i] = r.getPackageVisibilityOverride();
4067 showBadges[i] = r.canShowBadge();
4068 mRankingHelper.extractSignals(r);
4070 mRankingHelper.sort(mNotificationList);
4071 for (int i = 0; i < N; i++) {
4072 final NotificationRecord r = mNotificationList.get(i);
4074 || !orderBefore.get(i).equals(r.getKey())
4075 || visibilities[i] != r.getPackageVisibilityOverride()
4076 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
4077 || showBadges[i] != r.canShowBadge()) {
4078 scheduleSendRankingUpdate();
4085 @GuardedBy("mNotificationLock")
4086 private void recordCallerLocked(NotificationRecord record) {
4087 if (mZenModeHelper.isCall(record)) {
4088 mZenModeHelper.recordCaller(record);
4092 // let zen mode evaluate this record
4093 @GuardedBy("mNotificationLock")
4094 private void applyZenModeLocked(NotificationRecord record) {
4095 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4096 if (record.isIntercepted()) {
4097 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4098 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4099 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4100 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4101 record.setSuppressedVisualEffects(suppressed);
4103 record.setSuppressedVisualEffects(0);
4107 @GuardedBy("mNotificationLock")
4108 private int findNotificationRecordIndexLocked(NotificationRecord target) {
4109 return mRankingHelper.indexOf(mNotificationList, target);
4112 private void scheduleSendRankingUpdate() {
4113 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4114 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
4115 mHandler.sendMessage(m);
4119 private void handleSendRankingUpdate() {
4120 synchronized (mNotificationLock) {
4121 mListeners.notifyRankingUpdateLocked();
4125 private void scheduleListenerHintsChanged(int state) {
4126 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4127 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4130 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4131 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4132 mHandler.obtainMessage(
4133 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4134 listenerInterruptionFilter,
4138 private void handleListenerHintsChanged(int hints) {
4139 synchronized (mNotificationLock) {
4140 mListeners.notifyListenerHintsChangedLocked(hints);
4144 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4145 synchronized (mNotificationLock) {
4146 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4150 private final class WorkerHandler extends Handler
4152 public WorkerHandler(Looper looper) {
4157 public void handleMessage(Message msg)
4161 case MESSAGE_TIMEOUT:
4162 handleTimeout((ToastRecord)msg.obj);
4164 case MESSAGE_SAVE_POLICY_FILE:
4165 handleSavePolicyFile();
4167 case MESSAGE_SEND_RANKING_UPDATE:
4168 handleSendRankingUpdate();
4170 case MESSAGE_LISTENER_HINTS_CHANGED:
4171 handleListenerHintsChanged(msg.arg1);
4173 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4174 handleListenerInterruptionFilterChanged(msg.arg1);
4181 private final class RankingHandlerWorker extends Handler implements RankingHandler
4183 public RankingHandlerWorker(Looper looper) {
4188 public void handleMessage(Message msg) {
4190 case MESSAGE_RECONSIDER_RANKING:
4191 handleRankingReconsideration(msg);
4193 case MESSAGE_RANKING_SORT:
4194 handleRankingSort(msg);
4199 public void requestSort(boolean forceUpdate) {
4200 removeMessages(MESSAGE_RANKING_SORT);
4201 Message msg = Message.obtain();
4202 msg.what = MESSAGE_RANKING_SORT;
4203 msg.obj = forceUpdate;
4207 public void requestReconsideration(RankingReconsideration recon) {
4208 Message m = Message.obtain(this,
4209 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4210 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4211 sendMessageDelayed(m, delay);
4216 // ============================================================================
4217 static int clamp(int x, int low, int high) {
4218 return (x < low) ? low : ((x > high) ? high : x);
4221 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4222 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
4223 if (!manager.isEnabled()) {
4227 AccessibilityEvent event =
4228 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4229 event.setPackageName(packageName);
4230 event.setClassName(Notification.class.getName());
4231 event.setParcelableData(notification);
4232 CharSequence tickerText = notification.tickerText;
4233 if (!TextUtils.isEmpty(tickerText)) {
4234 event.getText().add(tickerText);
4237 manager.sendAccessibilityEvent(event);
4241 * Removes all NotificationsRecords with the same key as the given notification record
4242 * from both lists. Do not call this method while iterating over either list.
4244 @GuardedBy("mNotificationLock")
4245 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4246 // Remove from both lists, either list could have a separate Record for what is
4247 // effectively the same notification.
4248 boolean wasPosted = false;
4249 NotificationRecord recordInList = null;
4250 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4252 mNotificationList.remove(recordInList);
4253 mNotificationsByKey.remove(recordInList.sbn.getKey());
4256 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4258 mEnqueuedNotifications.remove(recordInList);
4263 @GuardedBy("mNotificationLock")
4264 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4265 boolean wasPosted) {
4266 final String canceledKey = r.getKey();
4269 recordCallerLocked(r);
4273 if (r.getNotification().deleteIntent != null) {
4275 r.getNotification().deleteIntent.send();
4276 } catch (PendingIntent.CanceledException ex) {
4277 // do nothing - there's no relevant way to recover, and
4278 // no reason to let this propagate
4279 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4284 // Only cancel these if this notification actually got to be posted.
4287 if (r.getNotification().getSmallIcon() != null) {
4288 if (reason != REASON_SNOOZED) {
4289 r.isCanceled = true;
4291 mListeners.notifyRemovedLocked(r.sbn, reason);
4292 mHandler.post(new Runnable() {
4295 mGroupHelper.onNotificationRemoved(r.sbn);
4301 if (canceledKey.equals(mSoundNotificationKey)) {
4302 mSoundNotificationKey = null;
4303 final long identity = Binder.clearCallingIdentity();
4305 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4306 if (player != null) {
4309 } catch (RemoteException e) {
4311 Binder.restoreCallingIdentity(identity);
4316 if (canceledKey.equals(mVibrateNotificationKey)) {
4317 mVibrateNotificationKey = null;
4318 long identity = Binder.clearCallingIdentity();
4323 Binder.restoreCallingIdentity(identity);
4328 mLights.remove(canceledKey);
4331 // Record usage stats
4332 // TODO: add unbundling stats?
4335 case REASON_CANCEL_ALL:
4336 case REASON_LISTENER_CANCEL:
4337 case REASON_LISTENER_CANCEL_ALL:
4338 mUsageStats.registerDismissedByUser(r);
4340 case REASON_APP_CANCEL:
4341 case REASON_APP_CANCEL_ALL:
4342 mUsageStats.registerRemovedByApp(r);
4346 String groupKey = r.getGroupKey();
4347 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4348 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4349 mSummaryByGroupKey.remove(groupKey);
4351 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4352 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4353 summaries.remove(r.sbn.getPackageName());
4356 // Save it for users of getHistoricalNotifications()
4357 mArchive.record(r.sbn);
4359 final long now = System.currentTimeMillis();
4360 MetricsLogger.action(r.getLogMaker(now)
4361 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4362 .setType(MetricsEvent.TYPE_DISMISS)
4363 .setSubtype(reason));
4364 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4365 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
4369 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4370 * and none of the {@code mustNotHaveFlags}.
4372 void cancelNotification(final int callingUid, final int callingPid,
4373 final String pkg, final String tag, final int id,
4374 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4375 final int userId, final int reason, final ManagedServiceInfo listener) {
4376 // In enqueueNotificationInternal notifications are added by scheduling the
4377 // work on the worker handler. Hence, we also schedule the cancel on this
4378 // handler to avoid a scenario where an add notification call followed by a
4379 // remove notification call ends up in not removing the notification.
4380 mHandler.post(new Runnable() {
4383 String listenerName = listener == null ? null : listener.component.toShortString();
4384 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4385 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4387 synchronized (mNotificationLock) {
4388 // Look for the notification, searching both the posted and enqueued lists.
4389 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4391 // The notification was found, check if it should be removed.
4393 // Ideally we'd do this in the caller of this method. However, that would
4394 // require the caller to also find the notification.
4395 if (reason == REASON_CLICK) {
4396 mUsageStats.registerClickedByUser(r);
4399 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4402 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4406 // Cancel the notification.
4407 boolean wasPosted = removeFromNotificationListsLocked(r);
4408 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
4409 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4411 updateLightsLocked();
4413 // No notification was found, assume that it is snoozed and cancel it.
4414 if (reason != REASON_SNOOZED) {
4415 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4427 * Determine whether the userId applies to the notification in question, either because
4428 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4430 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4432 // looking for USER_ALL notifications? match everything
4433 userId == UserHandle.USER_ALL
4434 // a notification sent to USER_ALL matches any query
4435 || r.getUserId() == UserHandle.USER_ALL
4436 // an exact user match
4437 || r.getUserId() == userId;
4441 * Determine whether the userId applies to the notification in question, either because
4442 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4443 * because it matches one of the users profiles.
4445 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4446 return notificationMatchesUserId(r, userId)
4447 || mUserProfiles.isCurrentProfile(r.getUserId());
4451 * Cancels all notifications from a given package that have all of the
4452 * {@code mustHaveFlags}.
4454 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4455 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4456 ManagedServiceInfo listener) {
4457 mHandler.post(new Runnable() {
4460 String listenerName = listener == null ? null : listener.component.toShortString();
4461 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4462 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4465 // Why does this parameter exist? Do we actually want to execute the above if doit
4471 synchronized (mNotificationLock) {
4472 FlagChecker flagChecker = (int flags) -> {
4473 if ((flags & mustHaveFlags) != mustHaveFlags) {
4476 if ((flags & mustNotHaveFlags) != 0) {
4482 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4483 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4484 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4485 listenerName, true /* wasPosted */);
4486 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4487 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4488 flagChecker, false /*includeCurrentProfiles*/, userId,
4489 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4490 mSnoozeHelper.cancel(userId, pkg);
4496 private interface FlagChecker {
4497 // Returns false if these flags do not pass the defined flag test.
4498 public boolean apply(int flags);
4501 @GuardedBy("mNotificationLock")
4502 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4503 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4504 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4505 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4506 ArrayList<NotificationRecord> canceledNotifications = null;
4507 for (int i = notificationList.size() - 1; i >= 0; --i) {
4508 NotificationRecord r = notificationList.get(i);
4509 if (includeCurrentProfiles) {
4510 if (!notificationMatchesCurrentProfiles(r, userId)) {
4513 } else if (!notificationMatchesUserId(r, userId)) {
4516 // Don't remove notifications to all, if there's no package name specified
4517 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4520 if (!flagChecker.apply(r.getFlags())) {
4523 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4526 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4530 if (canceledNotifications == null) {
4531 canceledNotifications = new ArrayList<>();
4533 notificationList.remove(i);
4534 mNotificationsByKey.remove(r.getKey());
4535 canceledNotifications.add(r);
4536 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
4538 if (canceledNotifications != null) {
4539 final int M = canceledNotifications.size();
4540 for (int i = 0; i < M; i++) {
4541 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4542 listenerName, false /* sendDelete */);
4544 updateLightsLocked();
4548 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4549 ManagedServiceInfo listener) {
4550 String listenerName = listener == null ? null : listener.component.toShortString();
4551 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4556 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4557 snoozeCriterionId, listenerName));
4559 // Needs to post so that it can cancel notifications not yet enqueued.
4560 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4563 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4564 String listenerName = listener == null ? null : listener.component.toShortString();
4566 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4568 mSnoozeHelper.repost(key);
4572 @GuardedBy("mNotificationLock")
4573 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4574 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4575 mHandler.post(new Runnable() {
4578 synchronized (mNotificationLock) {
4579 String listenerName =
4580 listener == null ? null : listener.component.toShortString();
4581 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4582 null, userId, 0, 0, reason, listenerName);
4584 FlagChecker flagChecker = (int flags) -> {
4585 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4592 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4593 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4594 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4595 listenerName, true);
4596 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4597 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4598 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4599 reason, listenerName, false);
4600 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4606 // Warning: The caller is responsible for invoking updateLightsLocked().
4607 @GuardedBy("mNotificationLock")
4608 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4609 String listenerName, boolean sendDelete) {
4610 Notification n = r.getNotification();
4611 if (!n.isGroupSummary()) {
4615 String pkg = r.sbn.getPackageName();
4618 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4622 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4624 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4625 listenerName, sendDelete, false);
4628 @GuardedBy("mNotificationLock")
4629 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4630 NotificationRecord parentNotification, int callingUid, int callingPid,
4631 String listenerName, boolean sendDelete, boolean wasPosted) {
4632 final String pkg = parentNotification.sbn.getPackageName();
4633 final int userId = parentNotification.getUserId();
4634 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4635 for (int i = notificationList.size() - 1; i >= 0; i--) {
4636 final NotificationRecord childR = notificationList.get(i);
4637 final StatusBarNotification childSbn = childR.sbn;
4638 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4639 childR.getGroupKey().equals(parentNotification.getGroupKey())
4640 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
4641 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4642 childSbn.getTag(), userId, 0, 0, reason, listenerName);
4643 notificationList.remove(i);
4644 mNotificationsByKey.remove(childR.getKey());
4645 cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
4650 @GuardedBy("mNotificationLock")
4651 void updateLightsLocked()
4653 // handle notification lights
4654 NotificationRecord ledNotification = null;
4655 while (ledNotification == null && !mLights.isEmpty()) {
4656 final String owner = mLights.get(mLights.size() - 1);
4657 ledNotification = mNotificationsByKey.get(owner);
4658 if (ledNotification == null) {
4659 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4660 mLights.remove(owner);
4664 // Don't flash while we are in a call or screen is on
4665 if (ledNotification == null || mInCall || mScreenOn) {
4666 mNotificationLight.turnOff();
4668 NotificationRecord.Light light = ledNotification.getLight();
4669 if (light != null && mNotificationPulseEnabled) {
4671 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4672 light.onMs, light.offMs);
4677 @GuardedBy("mNotificationLock")
4678 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4679 String groupKey, int userId) {
4680 List<NotificationRecord> records = new ArrayList<>();
4681 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4683 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4688 @GuardedBy("mNotificationLock")
4689 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4690 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4691 List<NotificationRecord> records = new ArrayList<>();
4692 final int len = list.size();
4693 for (int i = 0; i < len; i++) {
4694 NotificationRecord r = list.get(i);
4695 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4696 && r.sbn.getPackageName().equals(pkg)) {
4703 // Searches both enqueued and posted notifications by key.
4704 // TODO: need to combine a bunch of these getters with slightly different behavior.
4705 // TODO: Should enqueuing just add to mNotificationsByKey instead?
4706 @GuardedBy("mNotificationLock")
4707 private NotificationRecord findNotificationByKeyLocked(String key) {
4708 NotificationRecord r;
4709 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4712 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4718 @GuardedBy("mNotificationLock")
4719 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
4720 NotificationRecord r;
4721 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4724 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4731 @GuardedBy("mNotificationLock")
4732 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4733 String pkg, String tag, int id, int userId) {
4734 final int len = list.size();
4735 for (int i = 0; i < len; i++) {
4736 NotificationRecord r = list.get(i);
4737 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
4738 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
4745 @GuardedBy("mNotificationLock")
4746 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4748 final int N = list.size();
4749 for (int i = 0; i < N; i++) {
4750 if (key.equals(list.get(i).getKey())) {
4757 @GuardedBy("mNotificationLock")
4758 int indexOfNotificationLocked(String key) {
4759 final int N = mNotificationList.size();
4760 for (int i = 0; i < N; i++) {
4761 if (key.equals(mNotificationList.get(i).getKey())) {
4768 private void updateNotificationPulse() {
4769 synchronized (mNotificationLock) {
4770 updateLightsLocked();
4774 protected boolean isCallingUidSystem() {
4775 final int uid = Binder.getCallingUid();
4776 return uid == Process.SYSTEM_UID;
4779 protected boolean isUidSystemOrPhone(int uid) {
4780 final int appid = UserHandle.getAppId(uid);
4781 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
4784 // TODO: Most calls should probably move to isCallerSystem.
4785 protected boolean isCallerSystemOrPhone() {
4786 return isUidSystemOrPhone(Binder.getCallingUid());
4789 private void checkCallerIsSystem() {
4790 if (isCallerSystemOrPhone()) {
4793 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
4796 private void checkCallerIsSystemOrSameApp(String pkg) {
4797 if (isCallerSystemOrPhone()) {
4800 checkCallerIsSameApp(pkg);
4803 private boolean isCallerInstantApp(String pkg) {
4804 // System is always allowed to act for ephemeral apps.
4805 if (isCallerSystemOrPhone()) {
4809 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
4812 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
4813 UserHandle.getCallingUserId());
4815 throw new SecurityException("Unknown package " + pkg);
4817 return ai.isInstantApp();
4818 } catch (RemoteException re) {
4819 throw new SecurityException("Unknown package " + pkg, re);
4824 private void checkCallerIsSameApp(String pkg) {
4825 final int uid = Binder.getCallingUid();
4827 ApplicationInfo ai = mPackageManager.getApplicationInfo(
4828 pkg, 0, UserHandle.getCallingUserId());
4830 throw new SecurityException("Unknown package " + pkg);
4832 if (!UserHandle.isSameApp(ai.uid, uid)) {
4833 throw new SecurityException("Calling uid " + uid + " gave package "
4834 + pkg + " which is owned by uid " + ai.uid);
4836 } catch (RemoteException re) {
4837 throw new SecurityException("Unknown package " + pkg + "\n" + re);
4841 private static String callStateToString(int state) {
4843 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
4844 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
4845 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
4846 default: return "CALL_STATE_UNKNOWN_" + state;
4850 private void listenForCallState() {
4851 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
4853 public void onCallStateChanged(int state, String incomingNumber) {
4854 if (mCallState == state) return;
4855 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
4858 }, PhoneStateListener.LISTEN_CALL_STATE);
4862 * Generates a NotificationRankingUpdate from 'sbns', considering only
4863 * notifications visible to the given listener.
4865 @GuardedBy("mNotificationLock")
4866 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
4867 final int N = mNotificationList.size();
4868 ArrayList<String> keys = new ArrayList<String>(N);
4869 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
4870 ArrayList<Integer> importance = new ArrayList<>(N);
4871 Bundle overrideGroupKeys = new Bundle();
4872 Bundle visibilityOverrides = new Bundle();
4873 Bundle suppressedVisualEffects = new Bundle();
4874 Bundle explanation = new Bundle();
4875 Bundle channels = new Bundle();
4876 Bundle overridePeople = new Bundle();
4877 Bundle snoozeCriteria = new Bundle();
4878 Bundle showBadge = new Bundle();
4879 for (int i = 0; i < N; i++) {
4880 NotificationRecord record = mNotificationList.get(i);
4881 if (!isVisibleToListener(record.sbn, info)) {
4884 final String key = record.sbn.getKey();
4886 importance.add(record.getImportance());
4887 if (record.getImportanceExplanation() != null) {
4888 explanation.putCharSequence(key, record.getImportanceExplanation());
4890 if (record.isIntercepted()) {
4891 interceptedKeys.add(key);
4894 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
4895 if (record.getPackageVisibilityOverride()
4896 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
4897 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
4899 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
4900 channels.putParcelable(key, record.getChannel());
4901 overridePeople.putStringArrayList(key, record.getPeopleOverride());
4902 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
4903 showBadge.putBoolean(key, record.canShowBadge());
4905 final int M = keys.size();
4906 String[] keysAr = keys.toArray(new String[M]);
4907 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
4908 int[] importanceAr = new int[M];
4909 for (int i = 0; i < M; i++) {
4910 importanceAr[i] = importance.get(i);
4912 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
4913 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
4914 channels, overridePeople, snoozeCriteria, showBadge);
4917 boolean hasCompanionDevice(ManagedServiceInfo info) {
4918 if (mCompanionManager == null) {
4919 mCompanionManager = getCompanionManager();
4921 // Companion mgr doesn't exist on all device types
4922 if (mCompanionManager == null) {
4925 long identity = Binder.clearCallingIdentity();
4927 List<String> associations = mCompanionManager.getAssociations(
4928 info.component.getPackageName(), info.userid);
4929 if (!ArrayUtils.isEmpty(associations)) {
4932 } catch (SecurityException se) {
4933 // Not a privileged listener
4934 } catch (RemoteException re) {
4935 Slog.e(TAG, "Cannot reach companion device service", re);
4936 } catch (Exception e) {
4937 Slog.e(TAG, "Cannot verify listener " + info, e);
4939 Binder.restoreCallingIdentity(identity);
4944 protected ICompanionDeviceManager getCompanionManager() {
4945 return ICompanionDeviceManager.Stub.asInterface(
4946 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
4949 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
4950 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
4953 // TODO: remove this for older listeners.
4957 private boolean isPackageSuspendedForUser(String pkg, int uid) {
4958 int userId = UserHandle.getUserId(uid);
4960 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
4961 } catch (RemoteException re) {
4962 throw new SecurityException("Could not talk to package manager service");
4963 } catch (IllegalArgumentException ex) {
4964 // Package not found.
4969 private class TrimCache {
4970 StatusBarNotification heavy;
4971 StatusBarNotification sbnClone;
4972 StatusBarNotification sbnCloneLight;
4974 TrimCache(StatusBarNotification sbn) {
4978 StatusBarNotification ForListener(ManagedServiceInfo info) {
4979 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
4980 if (sbnCloneLight == null) {
4981 sbnCloneLight = heavy.cloneLight();
4983 return sbnCloneLight;
4985 if (sbnClone == null) {
4986 sbnClone = heavy.clone();
4993 public class NotificationAssistants extends ManagedServices {
4995 public NotificationAssistants() {
4996 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
5000 protected Config getConfig() {
5001 Config c = new Config();
5002 c.caption = "notification assistant service";
5003 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5004 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5005 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5006 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5007 c.clientLabel = R.string.notification_ranker_binding_label;
5012 protected IInterface asInterface(IBinder binder) {
5013 return INotificationListener.Stub.asInterface(binder);
5017 protected boolean checkType(IInterface service) {
5018 return service instanceof INotificationListener;
5022 protected void onServiceAdded(ManagedServiceInfo info) {
5023 mListeners.registerGuestService(info);
5027 @GuardedBy("mNotificationLock")
5028 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5029 mListeners.unregisterService(removed.service, removed.userid);
5032 public void onNotificationEnqueued(final NotificationRecord r) {
5033 final StatusBarNotification sbn = r.sbn;
5034 TrimCache trimCache = new TrimCache(sbn);
5036 // There should be only one, but it's a list, so while we enforce
5037 // singularity elsewhere, we keep it general here, to avoid surprises.
5038 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5039 boolean sbnVisible = isVisibleToListener(sbn, info);
5044 final int importance = r.getImportance();
5045 final boolean fromUser = r.isImportanceFromUser();
5046 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5047 mHandler.post(new Runnable() {
5050 notifyEnqueued(info, sbnToPost);
5056 private void notifyEnqueued(final ManagedServiceInfo info,
5057 final StatusBarNotification sbn) {
5058 final INotificationListener assistant = (INotificationListener) info.service;
5059 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5061 assistant.onNotificationEnqueued(sbnHolder);
5062 } catch (RemoteException ex) {
5063 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5068 * asynchronously notify the assistant that a notification has been snoozed until a
5071 @GuardedBy("mNotificationLock")
5072 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5073 final String snoozeCriterionId) {
5074 TrimCache trimCache = new TrimCache(sbn);
5075 for (final ManagedServiceInfo info : getServices()) {
5076 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5077 mHandler.post(new Runnable() {
5080 final INotificationListener assistant =
5081 (INotificationListener) info.service;
5082 StatusBarNotificationHolder sbnHolder
5083 = new StatusBarNotificationHolder(sbnToPost);
5085 assistant.onNotificationSnoozedUntilContext(
5086 sbnHolder, snoozeCriterionId);
5087 } catch (RemoteException ex) {
5088 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5095 public boolean isEnabled() {
5096 return !getServices().isEmpty();
5100 public class NotificationListeners extends ManagedServices {
5102 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5104 public NotificationListeners() {
5105 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
5109 protected Config getConfig() {
5110 Config c = new Config();
5111 c.caption = "notification listener";
5112 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5113 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5114 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5115 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5116 c.clientLabel = R.string.notification_listener_binding_label;
5121 protected IInterface asInterface(IBinder binder) {
5122 return INotificationListener.Stub.asInterface(binder);
5126 protected boolean checkType(IInterface service) {
5127 return service instanceof INotificationListener;
5131 public void onServiceAdded(ManagedServiceInfo info) {
5132 final INotificationListener listener = (INotificationListener) info.service;
5133 final NotificationRankingUpdate update;
5134 synchronized (mNotificationLock) {
5135 update = makeRankingUpdateLocked(info);
5138 listener.onListenerConnected(update);
5139 } catch (RemoteException e) {
5145 @GuardedBy("mNotificationLock")
5146 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5147 if (removeDisabledHints(removed)) {
5148 updateListenerHintsLocked();
5149 updateEffectsSuppressorLocked();
5151 mLightTrimListeners.remove(removed);
5154 @GuardedBy("mNotificationLock")
5155 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5156 if (trim == TRIM_LIGHT) {
5157 mLightTrimListeners.add(info);
5159 mLightTrimListeners.remove(info);
5163 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5164 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5168 * asynchronously notify all listeners about a new notification
5171 * Also takes care of removing a notification that has been visible to a listener before,
5172 * but isn't anymore.
5174 @GuardedBy("mNotificationLock")
5175 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5176 // Lazily initialized snapshots of the notification.
5177 TrimCache trimCache = new TrimCache(sbn);
5179 for (final ManagedServiceInfo info : getServices()) {
5180 boolean sbnVisible = isVisibleToListener(sbn, info);
5181 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5182 // This notification hasn't been and still isn't visible -> ignore.
5183 if (!oldSbnVisible && !sbnVisible) {
5186 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5188 // This notification became invisible -> remove the old one.
5189 if (oldSbnVisible && !sbnVisible) {
5190 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5191 mHandler.post(new Runnable() {
5194 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5200 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5201 mHandler.post(new Runnable() {
5204 notifyPosted(info, sbnToPost, update);
5211 * asynchronously notify all listeners about a removed notification
5213 @GuardedBy("mNotificationLock")
5214 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5215 // make a copy in case changes are made to the underlying Notification object
5216 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5218 final StatusBarNotification sbnLight = sbn.cloneLight();
5219 for (final ManagedServiceInfo info : getServices()) {
5220 if (!isVisibleToListener(sbn, info)) {
5223 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5224 mHandler.post(new Runnable() {
5227 notifyRemoved(info, sbnLight, update, reason);
5234 * asynchronously notify all listeners about a reordering of notifications
5236 @GuardedBy("mNotificationLock")
5237 public void notifyRankingUpdateLocked() {
5238 for (final ManagedServiceInfo serviceInfo : getServices()) {
5239 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5242 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5243 mHandler.post(new Runnable() {
5246 notifyRankingUpdate(serviceInfo, update);
5252 @GuardedBy("mNotificationLock")
5253 public void notifyListenerHintsChangedLocked(final int hints) {
5254 for (final ManagedServiceInfo serviceInfo : getServices()) {
5255 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5258 mHandler.post(new Runnable() {
5261 notifyListenerHintsChanged(serviceInfo, hints);
5267 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5268 for (final ManagedServiceInfo serviceInfo : getServices()) {
5269 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5272 mHandler.post(new Runnable() {
5275 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5281 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5282 final NotificationChannel channel, final int modificationType) {
5283 if (channel == null) {
5286 for (final ManagedServiceInfo serviceInfo : getServices()) {
5287 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5291 mHandler.post(new Runnable() {
5294 if (hasCompanionDevice(serviceInfo)) {
5295 notifyNotificationChannelChanged(
5296 serviceInfo, pkg, user, channel, modificationType);
5303 protected void notifyNotificationChannelGroupChanged(
5304 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5305 final int modificationType) {
5306 if (group == null) {
5309 for (final ManagedServiceInfo serviceInfo : getServices()) {
5310 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5314 mHandler.post(new Runnable() {
5317 if (hasCompanionDevice(serviceInfo)) {
5318 notifyNotificationChannelGroupChanged(
5319 serviceInfo, pkg, user, group, modificationType);
5326 private void notifyPosted(final ManagedServiceInfo info,
5327 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5328 final INotificationListener listener = (INotificationListener) info.service;
5329 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5331 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5332 } catch (RemoteException ex) {
5333 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5337 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5338 NotificationRankingUpdate rankingUpdate, int reason) {
5339 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5342 final INotificationListener listener = (INotificationListener) info.service;
5343 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5345 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5346 } catch (RemoteException ex) {
5347 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5351 private void notifyRankingUpdate(ManagedServiceInfo info,
5352 NotificationRankingUpdate rankingUpdate) {
5353 final INotificationListener listener = (INotificationListener) info.service;
5355 listener.onNotificationRankingUpdate(rankingUpdate);
5356 } catch (RemoteException ex) {
5357 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5361 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5362 final INotificationListener listener = (INotificationListener) info.service;
5364 listener.onListenerHintsChanged(hints);
5365 } catch (RemoteException ex) {
5366 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5370 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5371 int interruptionFilter) {
5372 final INotificationListener listener = (INotificationListener) info.service;
5374 listener.onInterruptionFilterChanged(interruptionFilter);
5375 } catch (RemoteException ex) {
5376 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5380 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5381 final String pkg, final UserHandle user, final NotificationChannel channel,
5382 final int modificationType) {
5383 final INotificationListener listener = (INotificationListener) info.service;
5385 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5386 } catch (RemoteException ex) {
5387 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5391 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5392 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5393 final int modificationType) {
5394 final INotificationListener listener = (INotificationListener) info.service;
5396 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5397 } catch (RemoteException ex) {
5398 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5402 public boolean isListenerPackage(String packageName) {
5403 if (packageName == null) {
5406 // TODO: clean up locking object later
5407 synchronized (mNotificationLock) {
5408 for (final ManagedServiceInfo serviceInfo : getServices()) {
5409 if (packageName.equals(serviceInfo.component.getPackageName())) {
5418 public static final class DumpFilter {
5419 public boolean filtered = false;
5420 public String pkgFilter;
5423 public boolean stats;
5424 public boolean redact = true;
5425 public boolean proto = false;
5427 public static DumpFilter parseFromArguments(String[] args) {
5428 final DumpFilter filter = new DumpFilter();
5429 for (int ai = 0; ai < args.length; ai++) {
5430 final String a = args[ai];
5431 if ("--proto".equals(args[0])) {
5432 filter.proto = true;
5434 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5435 filter.redact = false;
5436 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5437 if (ai < args.length-1) {
5439 filter.pkgFilter = args[ai].trim().toLowerCase();
5440 if (filter.pkgFilter.isEmpty()) {
5441 filter.pkgFilter = null;
5443 filter.filtered = true;
5446 } else if ("--zen".equals(a) || "zen".equals(a)) {
5447 filter.filtered = true;
5449 } else if ("--stats".equals(a)) {
5450 filter.stats = true;
5451 if (ai < args.length-1) {
5453 filter.since = Long.parseLong(args[ai]);
5462 public boolean matches(StatusBarNotification sbn) {
5463 if (!filtered) return true;
5464 return zen ? true : sbn != null
5465 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5468 public boolean matches(ComponentName component) {
5469 if (!filtered) return true;
5470 return zen ? true : component != null && matches(component.getPackageName());
5473 public boolean matches(String pkg) {
5474 if (!filtered) return true;
5475 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5479 public String toString() {
5480 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5485 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5486 * binder without sending large amounts of data over a oneway transaction.
5488 private static final class StatusBarNotificationHolder
5489 extends IStatusBarNotificationHolder.Stub {
5490 private StatusBarNotification mValue;
5492 public StatusBarNotificationHolder(StatusBarNotification value) {
5496 /** Get the held value and clear it. This function should only be called once per holder */
5498 public StatusBarNotification get() {
5499 StatusBarNotification value = mValue;
5505 private final class PolicyAccess {
5506 private static final String SEPARATOR = ":";
5507 private final String[] PERM = {
5508 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
5511 public boolean isPackageGranted(String pkg) {
5512 return pkg != null && getGrantedPackages().contains(pkg);
5515 public void put(String pkg, boolean granted) {
5516 if (pkg == null) return;
5517 final ArraySet<String> pkgs = getGrantedPackages();
5520 changed = pkgs.add(pkg);
5522 changed = pkgs.remove(pkg);
5524 if (!changed) return;
5525 final String setting = TextUtils.join(SEPARATOR, pkgs);
5526 final int currentUser = ActivityManager.getCurrentUser();
5527 Settings.Secure.putStringForUser(getContext().getContentResolver(),
5528 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5531 getContext().sendBroadcastAsUser(new Intent(NotificationManager
5532 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
5534 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
5537 public ArraySet<String> getGrantedPackages() {
5538 final ArraySet<String> pkgs = new ArraySet<>();
5540 long identity = Binder.clearCallingIdentity();
5542 final String setting = Settings.Secure.getStringForUser(
5543 getContext().getContentResolver(),
5544 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5545 ActivityManager.getCurrentUser());
5546 if (setting != null) {
5547 final String[] tokens = setting.split(SEPARATOR);
5548 for (int i = 0; i < tokens.length; i++) {
5549 String token = tokens[i];
5550 if (token != null) {
5551 token = token.trim();
5553 if (TextUtils.isEmpty(token)) {
5560 Binder.restoreCallingIdentity(identity);
5565 public String[] getRequestingPackages() throws RemoteException {
5566 final ParceledListSlice list = mPackageManager
5567 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
5568 ActivityManager.getCurrentUser());
5569 final List<PackageInfo> pkgs = list.getList();
5570 if (pkgs == null || pkgs.isEmpty()) return new String[0];
5571 final int N = pkgs.size();
5572 final String[] rt = new String[N];
5573 for (int i = 0; i < N; i++) {
5574 rt[i] = pkgs.get(i).packageName;