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_LOW;
20 import static android.app.NotificationManager.IMPORTANCE_MIN;
21 import static android.app.NotificationManager.IMPORTANCE_NONE;
22 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
23 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
24 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
25 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
26 import static android.os.UserHandle.USER_NULL;
27 import static android.service.notification.NotificationListenerService
28 .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
29 import static android.service.notification.NotificationListenerService
30 .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
31 import static android.service.notification.NotificationListenerService
32 .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
33 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
34 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
35 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
36 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
37 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
38 import static android.service.notification.NotificationListenerService.REASON_CLICK;
39 import static android.service.notification.NotificationListenerService.REASON_ERROR;
40 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
41 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
42 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
43 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
44 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
45 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
46 import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
47 import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
48 import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
49 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
50 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
51 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
52 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
53 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
54 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
55 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
56 import static android.service.notification.NotificationListenerService.TRIM_FULL;
57 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
59 import static android.view.Display.DEFAULT_DISPLAY;
60 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
62 import android.Manifest;
63 import android.annotation.NonNull;
64 import android.annotation.Nullable;
65 import android.app.ActivityManager;
66 import android.app.ActivityManagerInternal;
67 import android.app.AlarmManager;
68 import android.app.AppGlobals;
69 import android.app.AppOpsManager;
70 import android.app.AutomaticZenRule;
71 import android.app.NotificationChannelGroup;
72 import android.app.backup.BackupManager;
73 import android.app.IActivityManager;
74 import android.app.INotificationManager;
75 import android.app.ITransientNotification;
76 import android.app.Notification;
77 import android.app.NotificationChannel;
78 import android.app.NotificationManager.Policy;
79 import android.app.NotificationManager;
80 import android.app.PendingIntent;
81 import android.app.StatusBarManager;
82 import android.app.usage.UsageEvents;
83 import android.app.usage.UsageStatsManagerInternal;
84 import android.companion.ICompanionDeviceManager;
85 import android.content.BroadcastReceiver;
86 import android.content.ComponentName;
87 import android.content.ContentResolver;
88 import android.content.Context;
89 import android.content.Intent;
90 import android.content.IntentFilter;
91 import android.content.pm.ApplicationInfo;
92 import android.content.pm.IPackageManager;
93 import android.content.pm.PackageManager;
94 import android.content.pm.PackageManager.NameNotFoundException;
95 import android.content.pm.ParceledListSlice;
96 import android.content.res.Resources;
97 import android.database.ContentObserver;
98 import android.media.AudioAttributes;
99 import android.media.AudioManager;
100 import android.media.AudioManagerInternal;
101 import android.media.IRingtonePlayer;
102 import android.net.Uri;
103 import android.os.Binder;
104 import android.os.Build;
105 import android.os.Bundle;
106 import android.os.Environment;
107 import android.os.Handler;
108 import android.os.HandlerThread;
109 import android.os.IBinder;
110 import android.os.IInterface;
111 import android.os.Looper;
112 import android.os.Message;
113 import android.os.Process;
114 import android.os.RemoteException;
115 import android.os.ResultReceiver;
116 import android.os.ServiceManager;
117 import android.os.ShellCallback;
118 import android.os.ShellCommand;
119 import android.os.SystemClock;
120 import android.os.SystemProperties;
121 import android.os.UserHandle;
122 import android.os.Vibrator;
123 import android.os.VibrationEffect;
124 import android.provider.Settings;
125 import android.service.notification.Adjustment;
126 import android.service.notification.Condition;
127 import android.service.notification.IConditionProvider;
128 import android.service.notification.INotificationListener;
129 import android.service.notification.IStatusBarNotificationHolder;
130 import android.service.notification.NotificationAssistantService;
131 import android.service.notification.NotificationListenerService;
132 import android.service.notification.NotificationRankingUpdate;
133 import android.service.notification.NotificationRecordProto;
134 import android.service.notification.NotificationServiceDumpProto;
135 import android.service.notification.NotificationServiceProto;
136 import android.service.notification.SnoozeCriterion;
137 import android.service.notification.StatusBarNotification;
138 import android.service.notification.ZenModeConfig;
139 import android.service.notification.ZenModeProto;
140 import android.telecom.TelecomManager;
141 import android.telephony.PhoneStateListener;
142 import android.telephony.TelephonyManager;
143 import android.text.TextUtils;
144 import android.util.ArrayMap;
145 import android.util.ArraySet;
146 import android.util.AtomicFile;
147 import android.util.Log;
148 import android.util.Slog;
149 import android.util.SparseArray;
150 import android.util.Xml;
151 import android.util.proto.ProtoOutputStream;
152 import android.view.WindowManagerInternal;
153 import android.view.accessibility.AccessibilityEvent;
154 import android.view.accessibility.AccessibilityManager;
155 import android.widget.Toast;
157 import com.android.internal.R;
158 import com.android.internal.annotations.GuardedBy;
159 import com.android.internal.annotations.VisibleForTesting;
160 import com.android.internal.logging.MetricsLogger;
161 import com.android.internal.logging.nano.MetricsProto;
162 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
163 import com.android.internal.os.BackgroundThread;
164 import com.android.internal.statusbar.NotificationVisibility;
165 import com.android.internal.util.ArrayUtils;
166 import com.android.internal.util.DumpUtils;
167 import com.android.internal.util.FastXmlSerializer;
168 import com.android.internal.util.Preconditions;
169 import com.android.internal.util.XmlUtils;
170 import com.android.server.DeviceIdleController;
171 import com.android.server.EventLogTags;
172 import com.android.server.LocalServices;
173 import com.android.server.SystemService;
174 import com.android.server.lights.Light;
175 import com.android.server.lights.LightsManager;
176 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
177 import com.android.server.policy.PhoneWindowManager;
178 import com.android.server.statusbar.StatusBarManagerInternal;
179 import com.android.server.notification.ManagedServices.UserProfiles;
181 import libcore.io.IoUtils;
183 import org.json.JSONException;
184 import org.json.JSONObject;
185 import org.xmlpull.v1.XmlPullParser;
186 import org.xmlpull.v1.XmlPullParserException;
187 import org.xmlpull.v1.XmlSerializer;
189 import java.io.ByteArrayInputStream;
190 import java.io.ByteArrayOutputStream;
192 import java.io.FileDescriptor;
193 import java.io.FileNotFoundException;
194 import java.io.FileOutputStream;
195 import java.io.IOException;
196 import java.io.InputStream;
197 import java.io.OutputStream;
198 import java.io.PrintWriter;
200 import java.nio.charset.StandardCharsets;
201 import java.util.ArrayDeque;
202 import java.util.ArrayList;
203 import java.util.Arrays;
204 import java.util.Iterator;
205 import java.util.List;
206 import java.util.Map.Entry;
207 import java.util.Objects;
208 import java.util.Set;
209 import java.util.concurrent.TimeUnit;
212 public class NotificationManagerService extends SystemService {
213 static final String TAG = "NotificationService";
214 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
215 public static final boolean ENABLE_CHILD_NOTIFICATIONS
216 = SystemProperties.getBoolean("debug.child_notifs", true);
218 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
219 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
222 static final int MESSAGE_TIMEOUT = 2;
223 static final int MESSAGE_SAVE_POLICY_FILE = 3;
224 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
225 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
226 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
228 // ranking thread messages
229 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
230 private static final int MESSAGE_RANKING_SORT = 1001;
232 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
233 static final int SHORT_DELAY = 2000; // 2 seconds
235 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
237 static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
239 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
241 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
243 static final boolean ENABLE_BLOCKED_TOASTS = true;
245 // When #matchesCallFilter is called from the ringer, wait at most
246 // 3s to resolve the contacts. This timeout is required since
247 // ContactsProvider might take a long time to start up.
249 // Return STARRED_CONTACT when the timeout is hit in order to avoid
250 // missed calls in ZEN mode "Important".
251 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
252 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
253 ValidateNotificationPeople.STARRED_CONTACT;
255 /** notification_enqueue status value for a newly enqueued notification. */
256 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
258 /** notification_enqueue status value for an existing notification. */
259 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
261 /** notification_enqueue status value for an ignored notification. */
262 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
263 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
265 private static final long DELAY_FOR_ASSISTANT_TIME = 100;
267 private static final String ACTION_NOTIFICATION_TIMEOUT =
268 NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
269 private static final int REQUEST_CODE_TIMEOUT = 1;
270 private static final String SCHEME_TIMEOUT = "timeout";
271 private static final String EXTRA_KEY = "key";
273 private IActivityManager mAm;
274 private ActivityManager mActivityManager;
275 private IPackageManager mPackageManager;
276 private PackageManager mPackageManagerClient;
277 AudioManager mAudioManager;
278 AudioManagerInternal mAudioManagerInternal;
279 @Nullable StatusBarManagerInternal mStatusBar;
281 private WindowManagerInternal mWindowManagerInternal;
282 private AlarmManager mAlarmManager;
283 private ICompanionDeviceManager mCompanionManager;
284 private AccessibilityManager mAccessibilityManager;
286 final IBinder mForegroundToken = new Binder();
287 private WorkerHandler mHandler;
288 private final HandlerThread mRankingThread = new HandlerThread("ranker",
289 Process.THREAD_PRIORITY_BACKGROUND);
291 private Light mNotificationLight;
292 Light mAttentionLight;
294 private long[] mFallbackVibrationPattern;
295 private boolean mUseAttentionLight;
296 boolean mSystemReady;
298 private boolean mDisableNotificationEffects;
299 private int mCallState;
300 private String mSoundNotificationKey;
301 private String mVibrateNotificationKey;
303 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
305 private List<ComponentName> mEffectsSuppressors = new ArrayList<>();
306 private int mListenerHints; // right now, all hints are global
307 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
309 // for enabling and disabling notification pulse behavior
310 private boolean mScreenOn = true;
311 protected boolean mInCall = false;
312 private boolean mNotificationPulseEnabled;
314 private Uri mInCallNotificationUri;
315 private AudioAttributes mInCallNotificationAudioAttributes;
316 private float mInCallNotificationVolume;
318 // used as a mutex for access to all active notifications & listeners
319 final Object mNotificationLock = new Object();
320 @GuardedBy("mNotificationLock")
321 final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();
322 @GuardedBy("mNotificationLock")
323 final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();
324 @GuardedBy("mNotificationLock")
325 final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
326 @GuardedBy("mNotificationLock")
327 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
328 final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
329 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
331 // The last key in this list owns the hardware.
332 ArrayList<String> mLights = new ArrayList<>();
334 private AppOpsManager mAppOps;
335 private UsageStatsManagerInternal mAppUsageStats;
337 private Archive mArchive;
339 // Persistent storage for notification policy
340 private AtomicFile mPolicyFile;
342 private static final int DB_VERSION = 1;
344 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
345 private static final String ATTR_VERSION = "version";
347 private RankingHelper mRankingHelper;
349 private final UserProfiles mUserProfiles = new UserProfiles();
350 private NotificationListeners mListeners;
351 private NotificationAssistants mAssistants;
352 private ConditionProviders mConditionProviders;
353 private NotificationUsageStats mUsageStats;
355 private static final int MY_UID = Process.myUid();
356 private static final int MY_PID = Process.myPid();
357 private static final IBinder WHITELIST_TOKEN = new Binder();
358 private RankingHandler mRankingHandler;
359 private long mLastOverRateLogTime;
360 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
362 private SnoozeHelper mSnoozeHelper;
363 private GroupHelper mGroupHelper;
364 private boolean mIsTelevision;
366 private static class Archive {
367 final int mBufferSize;
368 final ArrayDeque<StatusBarNotification> mBuffer;
370 public Archive(int size) {
372 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
375 public String toString() {
376 final StringBuilder sb = new StringBuilder();
377 final int N = mBuffer.size();
378 sb.append("Archive (");
380 sb.append(" notification");
381 sb.append((N==1)?")":"s)");
382 return sb.toString();
385 public void record(StatusBarNotification nr) {
386 if (mBuffer.size() == mBufferSize) {
387 mBuffer.removeFirst();
390 // We don't want to store the heavy bits of the notification in the archive,
391 // but other clients in the system process might be using the object, so we
392 // store a (lightened) copy.
393 mBuffer.addLast(nr.cloneLight());
396 public Iterator<StatusBarNotification> descendingIterator() {
397 return mBuffer.descendingIterator();
400 public StatusBarNotification[] getArray(int count) {
401 if (count == 0) count = mBufferSize;
402 final StatusBarNotification[] a
403 = new StatusBarNotification[Math.min(count, mBuffer.size())];
404 Iterator<StatusBarNotification> iter = descendingIterator();
406 while (iter.hasNext() && i < count) {
407 a[i++] = iter.next();
414 protected void readDefaultApprovedServices(int userId) {
415 String defaultListenerAccess = getContext().getResources().getString(
416 com.android.internal.R.string.config_defaultListenerAccessPackages);
417 if (defaultListenerAccess != null) {
418 for (String whitelisted :
419 defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
420 // Gather all notification listener components for candidate pkgs.
421 Set<ComponentName> approvedListeners =
422 mListeners.queryPackageForServices(whitelisted,
423 PackageManager.MATCH_DIRECT_BOOT_AWARE
424 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
425 for (ComponentName cn : approvedListeners) {
427 getBinderService().setNotificationListenerAccessGrantedForUser(cn,
429 } catch (RemoteException e) {
435 String defaultDndAccess = getContext().getResources().getString(
436 com.android.internal.R.string.config_defaultDndAccessPackages);
437 if (defaultListenerAccess != null) {
438 for (String whitelisted :
439 defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
441 getBinderService().setNotificationPolicyAccessGranted(whitelisted, true);
442 } catch (RemoteException e) {
449 void readPolicyXml(InputStream stream, boolean forRestore)
450 throws XmlPullParserException, NumberFormatException, IOException {
451 final XmlPullParser parser = Xml.newPullParser();
452 parser.setInput(stream, StandardCharsets.UTF_8.name());
453 XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY);
454 boolean migratedManagedServices = false;
455 int outerDepth = parser.getDepth();
456 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
457 if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
458 mZenModeHelper.readXml(parser, forRestore);
459 } else if (RankingHelper.TAG_RANKING.equals(parser.getName())){
460 mRankingHelper.readXml(parser, forRestore);
462 // No non-system managed services are allowed on low ram devices
463 if (!ActivityManager.isLowRamDeviceStatic()) {
464 if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
465 mListeners.readXml(parser);
466 migratedManagedServices = true;
467 } else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) {
468 mAssistants.readXml(parser);
469 migratedManagedServices = true;
470 } else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) {
471 mConditionProviders.readXml(parser);
472 migratedManagedServices = true;
477 if (!migratedManagedServices) {
478 mListeners.migrateToXml();
479 mAssistants.migrateToXml();
480 mConditionProviders.migrateToXml();
485 private void loadPolicyFile() {
486 if (DBG) Slog.d(TAG, "loadPolicyFile");
487 synchronized (mPolicyFile) {
489 InputStream infile = null;
491 infile = mPolicyFile.openRead();
492 readPolicyXml(infile, false /*forRestore*/);
493 } catch (FileNotFoundException e) {
495 // Load default managed services approvals
496 readDefaultApprovedServices(UserHandle.USER_SYSTEM);
497 } catch (IOException e) {
498 Log.wtf(TAG, "Unable to read notification policy", e);
499 } catch (NumberFormatException e) {
500 Log.wtf(TAG, "Unable to parse notification policy", e);
501 } catch (XmlPullParserException e) {
502 Log.wtf(TAG, "Unable to parse notification policy", e);
504 IoUtils.closeQuietly(infile);
509 public void savePolicyFile() {
510 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
511 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
514 private void handleSavePolicyFile() {
515 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
516 synchronized (mPolicyFile) {
517 final FileOutputStream stream;
519 stream = mPolicyFile.startWrite();
520 } catch (IOException e) {
521 Slog.w(TAG, "Failed to save policy file", e);
526 writePolicyXml(stream, false /*forBackup*/);
527 mPolicyFile.finishWrite(stream);
528 } catch (IOException e) {
529 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
530 mPolicyFile.failWrite(stream);
533 BackupManager.dataChanged(getContext().getPackageName());
536 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
537 final XmlSerializer out = new FastXmlSerializer();
538 out.setOutput(stream, StandardCharsets.UTF_8.name());
539 out.startDocument(null, true);
540 out.startTag(null, TAG_NOTIFICATION_POLICY);
541 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
542 mZenModeHelper.writeXml(out, forBackup);
543 mRankingHelper.writeXml(out, forBackup);
544 mListeners.writeXml(out, forBackup);
545 mAssistants.writeXml(out, forBackup);
546 mConditionProviders.writeXml(out, forBackup);
547 out.endTag(null, TAG_NOTIFICATION_POLICY);
551 /** Use this to check if a package can post a notification or toast. */
552 private boolean checkNotificationOp(String pkg, int uid) {
553 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
554 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
557 private static final class ToastRecord
561 ITransientNotification callback;
565 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
569 this.callback = callback;
570 this.duration = duration;
574 void update(int duration) {
575 this.duration = duration;
578 void update(ITransientNotification callback) {
579 this.callback = callback;
582 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
583 if (filter != null && !filter.matches(pkg)) return;
584 pw.println(prefix + this);
588 public final String toString()
590 return "ToastRecord{"
591 + Integer.toHexString(System.identityHashCode(this))
593 + " callback=" + callback
594 + " duration=" + duration;
599 final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
602 public void onSetDisabled(int status) {
603 synchronized (mNotificationLock) {
604 mDisableNotificationEffects =
605 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
606 if (disableNotificationEffects(null) != null) {
607 // cancel whatever's going on
608 long identity = Binder.clearCallingIdentity();
610 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
611 if (player != null) {
614 } catch (RemoteException e) {
616 Binder.restoreCallingIdentity(identity);
619 identity = Binder.clearCallingIdentity();
623 Binder.restoreCallingIdentity(identity);
630 public void onClearAll(int callingUid, int callingPid, int userId) {
631 synchronized (mNotificationLock) {
632 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
633 /*includeCurrentProfiles*/ true);
638 public void onNotificationClick(int callingUid, int callingPid, String key) {
639 synchronized (mNotificationLock) {
640 NotificationRecord r = mNotificationsByKey.get(key);
642 Log.w(TAG, "No notification with key: " + key);
645 final long now = System.currentTimeMillis();
646 MetricsLogger.action(r.getLogMaker(now)
647 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
648 .setType(MetricsEvent.TYPE_ACTION));
649 EventLogTags.writeNotificationClicked(key,
650 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
652 StatusBarNotification sbn = r.sbn;
653 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
654 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
655 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
661 public void onNotificationActionClick(int callingUid, int callingPid, String key,
663 synchronized (mNotificationLock) {
664 NotificationRecord r = mNotificationsByKey.get(key);
666 Log.w(TAG, "No notification with key: " + key);
669 final long now = System.currentTimeMillis();
670 MetricsLogger.action(r.getLogMaker(now)
671 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
672 .setType(MetricsEvent.TYPE_ACTION)
673 .setSubtype(actionIndex));
674 EventLogTags.writeNotificationActionClicked(key, actionIndex,
675 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
676 // TODO: Log action click via UsageStats.
681 public void onNotificationClear(int callingUid, int callingPid,
682 String pkg, String tag, int id, int userId) {
683 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
684 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
685 true, userId, REASON_CANCEL, null);
689 public void onPanelRevealed(boolean clearEffects, int items) {
690 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
691 MetricsLogger.histogram(getContext(), "note_load", items);
692 EventLogTags.writeNotificationPanelRevealed(items);
699 public void onPanelHidden() {
700 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
701 EventLogTags.writeNotificationPanelHidden();
705 public void clearEffects() {
706 synchronized (mNotificationLock) {
707 if (DBG) Slog.d(TAG, "clearEffects");
709 clearVibrateLocked();
715 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
716 int uid, int initialPid, String message, int userId) {
717 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
718 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
719 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
721 long ident = Binder.clearCallingIdentity();
723 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
724 "Bad notification posted from package " + pkg
726 } catch (RemoteException e) {
728 Binder.restoreCallingIdentity(ident);
732 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
733 NotificationVisibility[] noLongerVisibleKeys) {
734 synchronized (mNotificationLock) {
735 for (NotificationVisibility nv : newlyVisibleKeys) {
736 NotificationRecord r = mNotificationsByKey.get(nv.key);
737 if (r == null) continue;
738 r.setVisibility(true, nv.rank);
741 // Note that we might receive this event after notifications
742 // have already left the system, e.g. after dismissing from the
743 // shade. Hence not finding notifications in
744 // mNotificationsByKey is not an exceptional condition.
745 for (NotificationVisibility nv : noLongerVisibleKeys) {
746 NotificationRecord r = mNotificationsByKey.get(nv.key);
747 if (r == null) continue;
748 r.setVisibility(false, nv.rank);
755 public void onNotificationExpansionChanged(String key,
756 boolean userAction, boolean expanded) {
757 synchronized (mNotificationLock) {
758 NotificationRecord r = mNotificationsByKey.get(key);
760 r.stats.onExpansionChanged(userAction, expanded);
761 final long now = System.currentTimeMillis();
763 MetricsLogger.action(r.getLogMaker(now)
764 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
765 .setType(expanded ? MetricsEvent.TYPE_DETAIL
766 : MetricsEvent.TYPE_COLLAPSE));
768 EventLogTags.writeNotificationExpansion(key,
769 userAction ? 1 : 0, expanded ? 1 : 0,
770 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
776 @GuardedBy("mNotificationLock")
777 private void clearSoundLocked() {
778 mSoundNotificationKey = null;
779 long identity = Binder.clearCallingIdentity();
781 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
782 if (player != null) {
785 } catch (RemoteException e) {
787 Binder.restoreCallingIdentity(identity);
791 @GuardedBy("mNotificationLock")
792 private void clearVibrateLocked() {
793 mVibrateNotificationKey = null;
794 long identity = Binder.clearCallingIdentity();
798 Binder.restoreCallingIdentity(identity);
802 @GuardedBy("mNotificationLock")
803 private void clearLightsLocked() {
806 updateLightsLocked();
809 protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
811 public void onReceive(Context context, Intent intent) {
812 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
813 mZenModeHelper.updateDefaultZenRules();
814 mRankingHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
819 private final BroadcastReceiver mRestoreReceiver = new BroadcastReceiver() {
821 public void onReceive(Context context, Intent intent) {
822 if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
824 String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
825 String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
826 int restoredFromSdkInt = intent.getIntExtra(
827 Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0);
828 mListeners.onSettingRestored(
829 element, newValue, restoredFromSdkInt, getSendingUserId());
830 mConditionProviders.onSettingRestored(
831 element, newValue, restoredFromSdkInt, getSendingUserId());
832 } catch (Exception e) {
833 Slog.wtf(TAG, "Cannot restore managed services from settings", e);
839 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
841 public void onReceive(Context context, Intent intent) {
842 String action = intent.getAction();
843 if (action == null) {
846 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
847 final NotificationRecord record;
848 synchronized (mNotificationLock) {
849 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
851 if (record != null) {
852 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
853 record.sbn.getPackageName(), record.sbn.getTag(),
854 record.sbn.getId(), 0,
855 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
856 REASON_TIMEOUT, null);
862 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
864 public void onReceive(Context context, Intent intent) {
865 String action = intent.getAction();
866 if (action == null) {
870 boolean queryRestart = false;
871 boolean queryRemove = false;
872 boolean packageChanged = false;
873 boolean cancelNotifications = true;
874 int reason = REASON_PACKAGE_CHANGED;
876 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
877 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
878 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
879 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
880 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
881 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
882 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
883 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
884 UserHandle.USER_ALL);
885 String pkgList[] = null;
886 int uidList[] = null;
887 boolean removingPackage = queryRemove &&
888 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
889 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
890 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
891 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
892 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
893 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
894 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
895 reason = REASON_PACKAGE_SUSPENDED;
896 } else if (queryRestart) {
897 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
898 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
900 Uri uri = intent.getData();
904 String pkgName = uri.getSchemeSpecificPart();
905 if (pkgName == null) {
908 if (packageChanged) {
909 // We cancel notifications for packages which have just been disabled
911 final int enabled = mPackageManager.getApplicationEnabledSetting(
913 changeUserId != UserHandle.USER_ALL ? changeUserId :
914 UserHandle.USER_SYSTEM);
915 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
916 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
917 cancelNotifications = false;
919 } catch (IllegalArgumentException e) {
920 // Package doesn't exist; probably racing with uninstall.
921 // cancelNotifications is already true, so nothing to do here.
923 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
925 } catch (RemoteException e) {
926 // Failed to talk to PackageManagerService Should never happen!
929 pkgList = new String[]{pkgName};
930 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
932 if (pkgList != null && (pkgList.length > 0)) {
933 for (String pkgName : pkgList) {
934 if (cancelNotifications) {
935 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
936 !queryRestart, changeUserId, reason, null);
940 mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
941 mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
942 mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
943 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
949 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
951 public void onReceive(Context context, Intent intent) {
952 String action = intent.getAction();
954 if (action.equals(Intent.ACTION_SCREEN_ON)) {
955 // Keep track of screen on/off state, but do not turn off the notification light
956 // until user passes through the lock screen or views the notification.
958 updateNotificationPulse();
959 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
961 updateNotificationPulse();
962 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
963 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
964 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
965 updateNotificationPulse();
966 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
967 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
968 if (userHandle >= 0) {
969 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
970 REASON_USER_STOPPED, null);
972 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
973 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
974 if (userHandle >= 0) {
975 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
976 REASON_PROFILE_TURNED_OFF, null);
978 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
979 // turn off LED when user passes through lock screen
980 mNotificationLight.turnOff();
981 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
982 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
983 // reload per-user settings
984 mSettingsObserver.update(null);
985 mUserProfiles.updateCache(context);
986 // Refresh managed services
987 mConditionProviders.onUserSwitched(user);
988 mListeners.onUserSwitched(user);
989 mAssistants.onUserSwitched(user);
990 mZenModeHelper.onUserSwitched(user);
991 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
992 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
993 if (userId != USER_NULL) {
994 mUserProfiles.updateCache(context);
995 if (!mUserProfiles.isManagedProfile(userId)) {
996 readDefaultApprovedServices(userId);
999 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
1000 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
1001 mZenModeHelper.onUserRemoved(user);
1002 mRankingHelper.onUserRemoved(user);
1003 mListeners.onUserRemoved(user);
1004 mConditionProviders.onUserRemoved(user);
1005 mAssistants.onUserRemoved(user);
1007 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
1008 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
1009 mConditionProviders.onUserUnlocked(user);
1010 mListeners.onUserUnlocked(user);
1011 mAssistants.onUserUnlocked(user);
1012 mZenModeHelper.onUserUnlocked(user);
1017 private final class SettingsObserver extends ContentObserver {
1018 private final Uri NOTIFICATION_BADGING_URI
1019 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
1020 private final Uri NOTIFICATION_LIGHT_PULSE_URI
1021 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1022 private final Uri NOTIFICATION_RATE_LIMIT_URI
1023 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
1025 SettingsObserver(Handler handler) {
1030 ContentResolver resolver = getContext().getContentResolver();
1031 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
1032 false, this, UserHandle.USER_ALL);
1033 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1034 false, this, UserHandle.USER_ALL);
1035 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
1036 false, this, UserHandle.USER_ALL);
1040 @Override public void onChange(boolean selfChange, Uri uri) {
1044 public void update(Uri uri) {
1045 ContentResolver resolver = getContext().getContentResolver();
1046 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1047 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
1048 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
1049 if (mNotificationPulseEnabled != pulseEnabled) {
1050 mNotificationPulseEnabled = pulseEnabled;
1051 updateNotificationPulse();
1054 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
1055 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
1056 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
1058 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
1059 mRankingHelper.updateBadgingEnabled();
1064 private SettingsObserver mSettingsObserver;
1065 protected ZenModeHelper mZenModeHelper;
1067 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1068 int[] ar = r.getIntArray(resid);
1072 final int len = ar.length > maxlen ? maxlen : ar.length;
1073 long[] out = new long[len];
1074 for (int i=0; i<len; i++) {
1080 public NotificationManagerService(Context context) {
1082 Notification.processWhitelistToken = WHITELIST_TOKEN;
1085 // TODO - replace these methods with a single VisibleForTesting constructor
1087 void setAudioManager(AudioManager audioMananger) {
1088 mAudioManager = audioMananger;
1092 void setVibrator(Vibrator vibrator) {
1093 mVibrator = vibrator;
1097 void setLights(Light light) {
1098 mNotificationLight = light;
1099 mAttentionLight = light;
1100 mNotificationPulseEnabled = true;
1104 void setScreenOn(boolean on) {
1109 int getNotificationRecordCount() {
1110 synchronized (mNotificationLock) {
1111 int count = mNotificationList.size() + mNotificationsByKey.size()
1112 + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
1113 // subtract duplicates
1114 for (NotificationRecord posted : mNotificationList) {
1115 if (mNotificationsByKey.containsKey(posted.getKey())) {
1118 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
1127 void clearNotifications() {
1128 mEnqueuedNotifications.clear();
1129 mNotificationList.clear();
1130 mNotificationsByKey.clear();
1131 mSummaryByGroupKey.clear();
1135 void addNotification(NotificationRecord r) {
1136 mNotificationList.add(r);
1137 mNotificationsByKey.put(r.sbn.getKey(), r);
1138 if (r.sbn.isGroup()) {
1139 mSummaryByGroupKey.put(r.getGroupKey(), r);
1144 void addEnqueuedNotification(NotificationRecord r) {
1145 mEnqueuedNotifications.add(r);
1149 NotificationRecord getNotificationRecord(String key) {
1150 return mNotificationsByKey.get(key);
1155 void setSystemReady(boolean systemReady) {
1156 mSystemReady = systemReady;
1160 void setHandler(WorkerHandler handler) {
1165 void setFallbackVibrationPattern(long[] vibrationPattern) {
1166 mFallbackVibrationPattern = vibrationPattern;
1170 void setPackageManager(IPackageManager packageManager) {
1171 mPackageManager = packageManager;
1175 void setRankingHelper(RankingHelper rankingHelper) {
1176 mRankingHelper = rankingHelper;
1180 void setRankingHandler(RankingHandler rankingHandler) {
1181 mRankingHandler = rankingHandler;
1185 void setIsTelevision(boolean isTelevision) {
1186 mIsTelevision = isTelevision;
1190 void setUsageStats(NotificationUsageStats us) {
1195 void setAccessibilityManager(AccessibilityManager am) {
1196 mAccessibilityManager = am;
1200 // TODO: All tests should use this init instead of the one-off setters above.
1202 void init(Looper looper, IPackageManager packageManager,
1203 PackageManager packageManagerClient,
1204 LightsManager lightsManager, NotificationListeners notificationListeners,
1205 NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
1206 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1207 NotificationUsageStats usageStats, AtomicFile policyFile,
1208 ActivityManager activityManager, GroupHelper groupHelper) {
1209 Resources resources = getContext().getResources();
1210 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1211 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1212 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1214 mAccessibilityManager =
1215 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1216 mAm = ActivityManager.getService();
1217 mPackageManager = packageManager;
1218 mPackageManagerClient = packageManagerClient;
1219 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1220 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1221 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1222 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1223 mCompanionManager = companionManager;
1224 mActivityManager = activityManager;
1226 mHandler = new WorkerHandler(looper);
1227 mRankingThread.start();
1228 String[] extractorNames;
1230 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1231 } catch (Resources.NotFoundException e) {
1232 extractorNames = new String[0];
1234 mUsageStats = usageStats;
1235 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1236 mRankingHelper = new RankingHelper(getContext(),
1237 mPackageManagerClient,
1241 mConditionProviders = conditionProviders;
1242 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1243 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1245 public void onConfigChanged() {
1250 void onZenModeChanged() {
1251 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1252 getContext().sendBroadcastAsUser(
1253 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1254 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1255 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1256 synchronized (mNotificationLock) {
1257 updateInterruptionFilterLocked();
1262 void onPolicyChanged() {
1263 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1266 mSnoozeHelper = snoozeHelper;
1267 mGroupHelper = groupHelper;
1269 // This is a ManagedServices object that keeps track of the listeners.
1270 mListeners = notificationListeners;
1272 // This is a MangedServices object that keeps track of the assistant.
1273 mAssistants = notificationAssistants;
1275 mPolicyFile = policyFile;
1278 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1279 if (mStatusBar != null) {
1280 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1283 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1284 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1286 mFallbackVibrationPattern = getLongArray(resources,
1287 R.array.config_notificationFallbackVibePattern,
1288 VIBRATE_PATTERN_MAXLEN,
1289 DEFAULT_VIBRATE_PATTERN);
1290 mInCallNotificationUri = Uri.parse("file://" +
1291 resources.getString(R.string.config_inCallNotificationSound));
1292 mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
1293 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
1294 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
1296 mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
1298 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1300 // Don't start allowing notifications until the setup wizard has run once.
1301 // After that, including subsequent boots, init with notifications turned on.
1302 // This works on the first boot because the setup wizard will toggle this
1303 // flag at least once and we'll go back to 0 after that.
1304 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1305 Settings.Global.DEVICE_PROVISIONED, 0)) {
1306 mDisableNotificationEffects = true;
1308 mZenModeHelper.initZenMode();
1309 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1311 mUserProfiles.updateCache(getContext());
1312 listenForCallState();
1314 mSettingsObserver = new SettingsObserver(mHandler);
1316 mArchive = new Archive(resources.getInteger(
1317 R.integer.config_notificationServiceArchiveSize));
1319 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1320 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1324 public void onStart() {
1325 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1327 public void repost(int userId, NotificationRecord r) {
1330 Slog.d(TAG, "Reposting " + r.getKey());
1332 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1333 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1334 r.sbn.getNotification(), userId);
1335 } catch (Exception e) {
1336 Slog.e(TAG, "Cannot un-snooze notification", e);
1341 final File systemDir = new File(Environment.getDataDirectory(), "system");
1343 init(Looper.myLooper(),
1344 AppGlobals.getPackageManager(), getContext().getPackageManager(),
1345 getLocalService(LightsManager.class),
1346 new NotificationListeners(AppGlobals.getPackageManager()),
1347 new NotificationAssistants(AppGlobals.getPackageManager()),
1348 new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
1349 null, snoozeHelper, new NotificationUsageStats(getContext()),
1350 new AtomicFile(new File(systemDir, "notification_policy.xml")),
1351 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
1354 // register for various Intents
1355 IntentFilter filter = new IntentFilter();
1356 filter.addAction(Intent.ACTION_SCREEN_ON);
1357 filter.addAction(Intent.ACTION_SCREEN_OFF);
1358 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1359 filter.addAction(Intent.ACTION_USER_PRESENT);
1360 filter.addAction(Intent.ACTION_USER_STOPPED);
1361 filter.addAction(Intent.ACTION_USER_SWITCHED);
1362 filter.addAction(Intent.ACTION_USER_ADDED);
1363 filter.addAction(Intent.ACTION_USER_REMOVED);
1364 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1365 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1366 getContext().registerReceiver(mIntentReceiver, filter);
1368 IntentFilter pkgFilter = new IntentFilter();
1369 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1370 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1371 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1372 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1373 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1374 pkgFilter.addDataScheme("package");
1375 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1378 IntentFilter suspendedPkgFilter = new IntentFilter();
1379 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1380 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1381 suspendedPkgFilter, null, null);
1383 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1384 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1387 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1388 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1389 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1391 IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
1392 getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
1394 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
1395 getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
1397 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1398 publishLocalService(NotificationManagerInternal.class, mInternalService);
1401 private GroupHelper getGroupHelper() {
1402 return new GroupHelper(new GroupHelper.Callback() {
1404 public void addAutoGroup(String key) {
1405 synchronized (mNotificationLock) {
1406 addAutogroupKeyLocked(key);
1411 public void removeAutoGroup(String key) {
1412 synchronized (mNotificationLock) {
1413 removeAutogroupKeyLocked(key);
1418 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1419 createAutoGroupSummary(userId, pkg, triggeringKey);
1423 public void removeAutoGroupSummary(int userId, String pkg) {
1424 synchronized (mNotificationLock) {
1425 clearAutogroupSummaryLocked(userId, pkg);
1431 private void sendRegisteredOnlyBroadcast(String action) {
1432 getContext().sendBroadcastAsUser(new Intent(action)
1433 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1437 public void onBootPhase(int phase) {
1438 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1439 // no beeping until we're basically done booting
1440 mSystemReady = true;
1442 // Grab our optional AudioService
1443 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1444 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1445 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1446 mZenModeHelper.onSystemReady();
1447 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1448 // This observer will force an update when observe is called, causing us to
1449 // bind to listener services.
1450 mSettingsObserver.observe();
1451 mListeners.onBootPhaseAppsCanStart();
1452 mAssistants.onBootPhaseAppsCanStart();
1453 mConditionProviders.onBootPhaseAppsCanStart();
1457 @GuardedBy("mNotificationLock")
1458 private void updateListenerHintsLocked() {
1459 final int hints = calculateHints();
1460 if (hints == mListenerHints) return;
1461 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1462 mListenerHints = hints;
1463 scheduleListenerHintsChanged(hints);
1466 @GuardedBy("mNotificationLock")
1467 private void updateEffectsSuppressorLocked() {
1468 final long updatedSuppressedEffects = calculateSuppressedEffects();
1469 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1470 final List<ComponentName> suppressors = getSuppressors();
1471 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1472 mEffectsSuppressors = suppressors;
1473 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1474 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1477 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1478 boolean fromListener) {
1479 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1481 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1482 UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
1484 if (isUidSystemOrPhone(uid)) {
1485 int[] profileIds = mUserProfiles.getCurrentProfileIds();
1486 int N = profileIds.length;
1487 for (int i = 0; i < N; i++) {
1488 int profileId = profileIds[i];
1489 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1490 profileId, REASON_CHANNEL_BANNED,
1495 mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
1497 if (!fromListener) {
1498 final NotificationChannel modifiedChannel =
1499 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1500 mListeners.notifyNotificationChannelChanged(
1501 pkg, UserHandle.getUserHandleForUid(uid),
1502 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1508 private ArrayList<ComponentName> getSuppressors() {
1509 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1510 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1511 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1513 for (ManagedServiceInfo info : serviceInfoList) {
1514 names.add(info.component);
1521 private boolean removeDisabledHints(ManagedServiceInfo info) {
1522 return removeDisabledHints(info, 0);
1525 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1526 boolean removed = false;
1528 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1529 final int hint = mListenersDisablingEffects.keyAt(i);
1530 final ArraySet<ManagedServiceInfo> listeners =
1531 mListenersDisablingEffects.valueAt(i);
1533 if (hints == 0 || (hint & hints) == hint) {
1534 removed = removed || listeners.remove(info);
1541 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1542 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1543 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1546 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1547 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1550 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1551 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1555 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1556 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1557 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1560 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1561 hintListeners.add(info);
1564 private int calculateHints() {
1566 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1567 int hint = mListenersDisablingEffects.keyAt(i);
1568 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1570 if (!serviceInfoList.isEmpty()) {
1578 private long calculateSuppressedEffects() {
1579 int hints = calculateHints();
1580 long suppressedEffects = 0;
1582 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1583 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1586 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1587 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1590 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1591 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1594 return suppressedEffects;
1597 @GuardedBy("mNotificationLock")
1598 private void updateInterruptionFilterLocked() {
1599 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1600 if (interruptionFilter == mInterruptionFilter) return;
1601 mInterruptionFilter = interruptionFilter;
1602 scheduleInterruptionFilterChanged(interruptionFilter);
1606 INotificationManager getBinderService() {
1607 return INotificationManager.Stub.asInterface(mService);
1611 NotificationManagerInternal getInternalService() {
1612 return mInternalService;
1615 private final IBinder mService = new INotificationManager.Stub() {
1617 // ============================================================================
1620 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1623 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1624 + " duration=" + duration);
1627 if (pkg == null || callback == null) {
1628 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1631 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1632 final boolean isPackageSuspended =
1633 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1635 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1636 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1637 || isPackageSuspended)) {
1638 Slog.e(TAG, "Suppressing toast from package " + pkg
1639 + (isPackageSuspended
1640 ? " due to package suspended by administrator."
1641 : " by user request."));
1645 synchronized (mToastQueue) {
1646 int callingPid = Binder.getCallingPid();
1647 long callingId = Binder.clearCallingIdentity();
1651 // All packages aside from the android package can enqueue one toast at a time
1652 if (!isSystemToast) {
1653 index = indexOfToastPackageLocked(pkg);
1655 index = indexOfToastLocked(pkg, callback);
1658 // If the package already has a toast, we update its toast
1659 // in the queue, we don't move it to the end of the queue.
1661 record = mToastQueue.get(index);
1662 record.update(duration);
1663 record.update(callback);
1665 Binder token = new Binder();
1666 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1667 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1668 mToastQueue.add(record);
1669 index = mToastQueue.size() - 1;
1671 keepProcessAliveIfNeededLocked(callingPid);
1672 // If it's at index 0, it's the current toast. It doesn't matter if it's
1673 // new or just been updated. Call back and tell it to show itself.
1674 // If the callback fails, this will remove it from the list, so don't
1675 // assume that it's valid after this.
1677 showNextToastLocked();
1680 Binder.restoreCallingIdentity(callingId);
1686 public void cancelToast(String pkg, ITransientNotification callback) {
1687 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1689 if (pkg == null || callback == null) {
1690 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1694 synchronized (mToastQueue) {
1695 long callingId = Binder.clearCallingIdentity();
1697 int index = indexOfToastLocked(pkg, callback);
1699 cancelToastLocked(index);
1701 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1702 + " callback=" + callback);
1705 Binder.restoreCallingIdentity(callingId);
1711 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1712 Notification notification, int userId) throws RemoteException {
1713 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1714 Binder.getCallingPid(), tag, id, notification, userId);
1718 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1719 checkCallerIsSystemOrSameApp(pkg);
1720 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1721 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1722 // Don't allow client applications to cancel foreground service notis or autobundled
1724 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1725 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1726 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1727 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1731 public void cancelAllNotifications(String pkg, int userId) {
1732 checkCallerIsSystemOrSameApp(pkg);
1734 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1735 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1737 // Calling from user space, don't allow the canceling of actively
1738 // running foreground services.
1739 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1740 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1741 REASON_APP_CANCEL_ALL, null);
1745 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1746 checkCallerIsSystem();
1748 mRankingHelper.setEnabled(pkg, uid, enabled);
1749 // Now, cancel any outstanding notifications that are part of a just-disabled app
1751 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1752 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1758 * Use this when you just want to know if notifications are OK for this package.
1761 public boolean areNotificationsEnabled(String pkg) {
1762 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1766 * Use this when you just want to know if notifications are OK for this package.
1769 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1770 checkCallerIsSystemOrSameApp(pkg);
1772 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1776 public int getPackageImportance(String pkg) {
1777 checkCallerIsSystemOrSameApp(pkg);
1778 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1782 public boolean canShowBadge(String pkg, int uid) {
1783 checkCallerIsSystem();
1784 return mRankingHelper.canShowBadge(pkg, uid);
1788 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1789 checkCallerIsSystem();
1790 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1795 public void createNotificationChannelGroups(String pkg,
1796 ParceledListSlice channelGroupList) throws RemoteException {
1797 checkCallerIsSystemOrSameApp(pkg);
1798 List<NotificationChannelGroup> groups = channelGroupList.getList();
1799 final int groupSize = groups.size();
1800 for (int i = 0; i < groupSize; i++) {
1801 final NotificationChannelGroup group = groups.get(i);
1802 Preconditions.checkNotNull(group, "group in list is null");
1803 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1804 true /* fromTargetApp */);
1805 mListeners.notifyNotificationChannelGroupChanged(pkg,
1806 UserHandle.of(UserHandle.getCallingUserId()), group,
1807 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1812 private void createNotificationChannelsImpl(String pkg, int uid,
1813 ParceledListSlice channelsList) {
1814 List<NotificationChannel> channels = channelsList.getList();
1815 final int channelsSize = channels.size();
1816 for (int i = 0; i < channelsSize; i++) {
1817 final NotificationChannel channel = channels.get(i);
1818 Preconditions.checkNotNull(channel, "channel in list is null");
1819 mRankingHelper.createNotificationChannel(pkg, uid, channel,
1820 true /* fromTargetApp */);
1821 mListeners.notifyNotificationChannelChanged(pkg,
1822 UserHandle.getUserHandleForUid(uid),
1823 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1824 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1830 public void createNotificationChannels(String pkg,
1831 ParceledListSlice channelsList) throws RemoteException {
1832 checkCallerIsSystemOrSameApp(pkg);
1833 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1837 public void createNotificationChannelsForPackage(String pkg, int uid,
1838 ParceledListSlice channelsList) throws RemoteException {
1839 checkCallerIsSystem();
1840 createNotificationChannelsImpl(pkg, uid, channelsList);
1844 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1845 checkCallerIsSystemOrSameApp(pkg);
1846 return mRankingHelper.getNotificationChannel(
1847 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1851 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1852 String channelId, boolean includeDeleted) {
1853 checkCallerIsSystem();
1854 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1858 public void deleteNotificationChannel(String pkg, String channelId) {
1859 checkCallerIsSystemOrSameApp(pkg);
1860 final int callingUid = Binder.getCallingUid();
1861 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1862 throw new IllegalArgumentException("Cannot delete default channel");
1864 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1865 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1866 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1867 mListeners.notifyNotificationChannelChanged(pkg,
1868 UserHandle.getUserHandleForUid(callingUid),
1869 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1870 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1875 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1877 checkCallerIsSystemOrSameApp(pkg);
1878 return new ParceledListSlice<>(new ArrayList(
1879 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1883 public void deleteNotificationChannelGroup(String pkg, String groupId) {
1884 checkCallerIsSystemOrSameApp(pkg);
1886 final int callingUid = Binder.getCallingUid();
1887 NotificationChannelGroup groupToDelete =
1888 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1889 if (groupToDelete != null) {
1890 List<NotificationChannel> deletedChannels =
1891 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1892 for (int i = 0; i < deletedChannels.size(); i++) {
1893 final NotificationChannel deletedChannel = deletedChannels.get(i);
1894 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1896 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1898 mListeners.notifyNotificationChannelChanged(pkg,
1899 UserHandle.getUserHandleForUid(callingUid),
1901 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1903 mListeners.notifyNotificationChannelGroupChanged(
1904 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1905 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1911 public void updateNotificationChannelForPackage(String pkg, int uid,
1912 NotificationChannel channel) {
1913 enforceSystemOrSystemUI("Caller not system or systemui");
1914 Preconditions.checkNotNull(channel);
1915 updateNotificationChannelInt(pkg, uid, channel, false);
1919 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1920 int uid, boolean includeDeleted) {
1921 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1922 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1926 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1927 boolean includeDeleted) {
1928 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1929 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1934 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1935 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1936 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1940 public int getDeletedChannelCount(String pkg, int uid) {
1941 enforceSystemOrSystemUI("getDeletedChannelCount");
1942 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1946 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1947 String pkg, int uid, boolean includeDeleted) {
1948 checkCallerIsSystem();
1949 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1953 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1954 String groupId, String pkg, int uid) {
1955 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1956 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1960 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1961 checkCallerIsSystemOrSameApp(pkg);
1962 return mRankingHelper.getNotificationChannels(
1963 pkg, Binder.getCallingUid(), false /* includeDeleted */);
1967 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1968 checkCallerIsSystem();
1970 // Cancel posted notifications
1971 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1972 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1974 final String[] packages = new String[] {packageName};
1975 final int[] uids = new int[] {uid};
1977 // Listener & assistant
1978 mListeners.onPackagesChanged(true, packages, uids);
1979 mAssistants.onPackagesChanged(true, packages, uids);
1982 mConditionProviders.onPackagesChanged(true, packages, uids);
1984 // Reset notification preferences
1986 mRankingHelper.onPackagesChanged(
1987 true, UserHandle.getCallingUserId(), packages, uids);
1995 * System-only API for getting a list of current (i.e. not cleared) notifications.
1997 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1998 * @returns A list of all the notifications, in natural order.
2001 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
2002 // enforce() will ensure the calling uid has the correct permission
2003 getContext().enforceCallingOrSelfPermission(
2004 android.Manifest.permission.ACCESS_NOTIFICATIONS,
2005 "NotificationManagerService.getActiveNotifications");
2007 StatusBarNotification[] tmp = null;
2008 int uid = Binder.getCallingUid();
2010 // noteOp will check to make sure the callingPkg matches the uid
2011 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
2012 == AppOpsManager.MODE_ALLOWED) {
2013 synchronized (mNotificationLock) {
2014 tmp = new StatusBarNotification[mNotificationList.size()];
2015 final int N = mNotificationList.size();
2016 for (int i=0; i<N; i++) {
2017 tmp[i] = mNotificationList.get(i).sbn;
2025 * Public API for getting a list of current notifications for the calling package/uid.
2027 * Note that since notification posting is done asynchronously, this will not return
2028 * notifications that are in the process of being posted.
2030 * @returns A list of all the package's notifications, in natural order.
2033 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
2034 int incomingUserId) {
2035 checkCallerIsSystemOrSameApp(pkg);
2036 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2037 Binder.getCallingUid(), incomingUserId, true, false,
2038 "getAppActiveNotifications", pkg);
2039 synchronized (mNotificationLock) {
2040 final ArrayMap<String, StatusBarNotification> map
2041 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
2042 final int N = mNotificationList.size();
2043 for (int i = 0; i < N; i++) {
2044 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2045 mNotificationList.get(i).sbn);
2047 map.put(sbn.getKey(), sbn);
2050 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
2051 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
2053 map.put(sbn.getKey(), sbn);
2056 final int M = mEnqueuedNotifications.size();
2057 for (int i = 0; i < M; i++) {
2058 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2059 mEnqueuedNotifications.get(i).sbn);
2061 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
2064 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
2065 list.addAll(map.values());
2066 return new ParceledListSlice<StatusBarNotification>(list);
2070 private StatusBarNotification sanitizeSbn(String pkg, int userId,
2071 StatusBarNotification sbn) {
2072 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
2073 // We could pass back a cloneLight() but clients might get confused and
2074 // try to send this thing back to notify() again, which would not work
2076 return new StatusBarNotification(
2077 sbn.getPackageName(),
2079 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
2080 sbn.getNotification().clone(),
2081 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
2087 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
2089 * Requires ACCESS_NOTIFICATIONS which is signature|system.
2092 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
2093 // enforce() will ensure the calling uid has the correct permission
2094 getContext().enforceCallingOrSelfPermission(
2095 android.Manifest.permission.ACCESS_NOTIFICATIONS,
2096 "NotificationManagerService.getHistoricalNotifications");
2098 StatusBarNotification[] tmp = null;
2099 int uid = Binder.getCallingUid();
2101 // noteOp will check to make sure the callingPkg matches the uid
2102 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
2103 == AppOpsManager.MODE_ALLOWED) {
2104 synchronized (mArchive) {
2105 tmp = mArchive.getArray(count);
2112 * Register a listener binder directly with the notification manager.
2114 * Only works with system callers. Apps should extend
2115 * {@link android.service.notification.NotificationListenerService}.
2118 public void registerListener(final INotificationListener listener,
2119 final ComponentName component, final int userid) {
2120 enforceSystemOrSystemUI("INotificationManager.registerListener");
2121 mListeners.registerService(listener, component, userid);
2125 * Remove a listener binder directly
2128 public void unregisterListener(INotificationListener token, int userid) {
2129 mListeners.unregisterService(token, userid);
2133 * Allow an INotificationListener to simulate a "clear all" operation.
2135 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2137 * @param token The binder for the listener, to check that the caller is allowed
2140 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
2141 final int callingUid = Binder.getCallingUid();
2142 final int callingPid = Binder.getCallingPid();
2143 long identity = Binder.clearCallingIdentity();
2145 synchronized (mNotificationLock) {
2146 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2148 final int N = keys.length;
2149 for (int i = 0; i < N; i++) {
2150 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2151 if (r == null) continue;
2152 final int userId = r.sbn.getUserId();
2153 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2154 !mUserProfiles.isCurrentProfile(userId)) {
2155 throw new SecurityException("Disallowed call from listener: "
2158 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2159 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2163 cancelAllLocked(callingUid, callingPid, info.userid,
2164 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
2168 Binder.restoreCallingIdentity(identity);
2173 * Handle request from an approved listener to re-enable itself.
2175 * @param component The componenet to be re-enabled, caller must match package.
2178 public void requestBindListener(ComponentName component) {
2179 checkCallerIsSystemOrSameApp(component.getPackageName());
2180 long identity = Binder.clearCallingIdentity();
2182 ManagedServices manager =
2183 mAssistants.isComponentEnabledForCurrentProfiles(component)
2186 manager.setComponentState(component, true);
2188 Binder.restoreCallingIdentity(identity);
2193 public void requestUnbindListener(INotificationListener token) {
2194 long identity = Binder.clearCallingIdentity();
2196 // allow bound services to disable themselves
2197 synchronized (mNotificationLock) {
2198 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2199 info.getOwner().setComponentState(info.component, false);
2202 Binder.restoreCallingIdentity(identity);
2207 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2208 long identity = Binder.clearCallingIdentity();
2210 synchronized (mNotificationLock) {
2211 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2213 final int N = keys.length;
2214 for (int i = 0; i < N; i++) {
2215 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2216 if (r == null) continue;
2217 final int userId = r.sbn.getUserId();
2218 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2219 !mUserProfiles.isCurrentProfile(userId)) {
2220 throw new SecurityException("Disallowed call from listener: "
2224 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2225 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2226 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2228 UsageEvents.Event.USER_INTERACTION);
2235 Binder.restoreCallingIdentity(identity);
2240 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2242 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2244 * @param info The binder for the listener, to check that the caller is allowed
2246 @GuardedBy("mNotificationLock")
2247 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2248 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2249 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2250 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2252 userId, REASON_LISTENER_CANCEL, info);
2256 * Allow an INotificationListener to snooze a single notification until a context.
2258 * @param token The binder for the listener, to check that the caller is allowed
2261 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2262 String key, String snoozeCriterionId) {
2263 long identity = Binder.clearCallingIdentity();
2265 synchronized (mNotificationLock) {
2266 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2267 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2270 Binder.restoreCallingIdentity(identity);
2275 * Allow an INotificationListener to snooze a single notification until a time.
2277 * @param token The binder for the listener, to check that the caller is allowed
2280 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2282 long identity = Binder.clearCallingIdentity();
2284 synchronized (mNotificationLock) {
2285 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2286 snoozeNotificationInt(key, duration, null, info);
2289 Binder.restoreCallingIdentity(identity);
2294 * Allows the notification assistant to un-snooze a single notification.
2296 * @param token The binder for the assistant, to check that the caller is allowed
2299 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2300 long identity = Binder.clearCallingIdentity();
2302 synchronized (mNotificationLock) {
2303 final ManagedServiceInfo info =
2304 mAssistants.checkServiceTokenLocked(token);
2305 unsnoozeNotificationInt(key, info);
2308 Binder.restoreCallingIdentity(identity);
2313 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2315 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2317 * @param token The binder for the listener, to check that the caller is allowed
2320 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2321 String tag, int id) {
2322 final int callingUid = Binder.getCallingUid();
2323 final int callingPid = Binder.getCallingPid();
2324 long identity = Binder.clearCallingIdentity();
2326 synchronized (mNotificationLock) {
2327 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2328 if (info.supportsProfiles()) {
2329 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2330 + "from " + info.component
2331 + " use cancelNotification(key) instead.");
2333 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2334 pkg, tag, id, info.userid);
2338 Binder.restoreCallingIdentity(identity);
2343 * Allow an INotificationListener to request the list of outstanding notifications seen by
2344 * the current user. Useful when starting up, after which point the listener callbacks
2347 * @param token The binder for the listener, to check that the caller is allowed
2348 * @param keys An array of notification keys to fetch, or null to fetch everything
2349 * @returns The return value will contain the notifications specified in keys, in that
2350 * order, or if keys is null, all the notifications, in natural order.
2353 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2354 INotificationListener token, String[] keys, int trim) {
2355 synchronized (mNotificationLock) {
2356 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2357 final boolean getKeys = keys != null;
2358 final int N = getKeys ? keys.length : mNotificationList.size();
2359 final ArrayList<StatusBarNotification> list
2360 = new ArrayList<StatusBarNotification>(N);
2361 for (int i=0; i<N; i++) {
2362 final NotificationRecord r = getKeys
2363 ? mNotificationsByKey.get(keys[i])
2364 : mNotificationList.get(i);
2365 if (r == null) continue;
2366 StatusBarNotification sbn = r.sbn;
2367 if (!isVisibleToListener(sbn, info)) continue;
2368 StatusBarNotification sbnToSend =
2369 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2370 list.add(sbnToSend);
2372 return new ParceledListSlice<StatusBarNotification>(list);
2377 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2378 * seen by the current user. Useful when starting up, after which point the listener
2379 * callbacks should be used.
2381 * @param token The binder for the listener, to check that the caller is allowed
2382 * @returns The return value will contain the notifications specified in keys, in that
2383 * order, or if keys is null, all the notifications, in natural order.
2386 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2387 INotificationListener token, int trim) {
2388 synchronized (mNotificationLock) {
2389 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2390 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2391 final int N = snoozedRecords.size();
2392 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2393 for (int i=0; i < N; i++) {
2394 final NotificationRecord r = snoozedRecords.get(i);
2395 if (r == null) continue;
2396 StatusBarNotification sbn = r.sbn;
2397 if (!isVisibleToListener(sbn, info)) continue;
2398 StatusBarNotification sbnToSend =
2399 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2400 list.add(sbnToSend);
2402 return new ParceledListSlice<>(list);
2407 public void requestHintsFromListener(INotificationListener token, int hints) {
2408 final long identity = Binder.clearCallingIdentity();
2410 synchronized (mNotificationLock) {
2411 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2412 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2413 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2414 | HINT_HOST_DISABLE_CALL_EFFECTS;
2415 final boolean disableEffects = (hints & disableEffectsMask) != 0;
2416 if (disableEffects) {
2417 addDisabledHints(info, hints);
2419 removeDisabledHints(info, hints);
2421 updateListenerHintsLocked();
2422 updateEffectsSuppressorLocked();
2425 Binder.restoreCallingIdentity(identity);
2430 public int getHintsFromListener(INotificationListener token) {
2431 synchronized (mNotificationLock) {
2432 return mListenerHints;
2437 public void requestInterruptionFilterFromListener(INotificationListener token,
2438 int interruptionFilter) throws RemoteException {
2439 final long identity = Binder.clearCallingIdentity();
2441 synchronized (mNotificationLock) {
2442 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2443 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2444 updateInterruptionFilterLocked();
2447 Binder.restoreCallingIdentity(identity);
2452 public int getInterruptionFilterFromListener(INotificationListener token)
2453 throws RemoteException {
2454 synchronized (mNotificationLight) {
2455 return mInterruptionFilter;
2460 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2461 throws RemoteException {
2462 synchronized (mNotificationLock) {
2463 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2464 if (info == null) return;
2465 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2470 public int getZenMode() {
2471 return mZenModeHelper.getZenMode();
2475 public ZenModeConfig getZenModeConfig() {
2476 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2477 return mZenModeHelper.getConfig();
2481 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2482 enforceSystemOrSystemUI("INotificationManager.setZenMode");
2483 final long identity = Binder.clearCallingIdentity();
2485 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2487 Binder.restoreCallingIdentity(identity);
2492 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2493 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2494 return mZenModeHelper.getZenRules();
2498 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2499 Preconditions.checkNotNull(id, "Id is null");
2500 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2501 return mZenModeHelper.getAutomaticZenRule(id);
2505 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2506 throws RemoteException {
2507 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2508 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2509 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2510 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2511 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2513 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2514 "addAutomaticZenRule");
2518 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2519 throws RemoteException {
2520 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2521 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2522 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2523 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2524 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2526 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2527 "updateAutomaticZenRule");
2531 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2532 Preconditions.checkNotNull(id, "Id is null");
2533 // Verify that they can modify zen rules.
2534 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2536 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2540 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2541 Preconditions.checkNotNull(packageName, "Package name is null");
2542 enforceSystemOrSystemUI("removeAutomaticZenRules");
2544 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2548 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2549 Preconditions.checkNotNull(owner, "Owner is null");
2550 enforceSystemOrSystemUI("getRuleInstanceCount");
2552 return mZenModeHelper.getCurrentInstanceCount(owner);
2556 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2557 enforcePolicyAccess(pkg, "setInterruptionFilter");
2558 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2559 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2560 final long identity = Binder.clearCallingIdentity();
2562 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2564 Binder.restoreCallingIdentity(identity);
2569 public void notifyConditions(final String pkg, IConditionProvider provider,
2570 final Condition[] conditions) {
2571 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2572 checkCallerIsSystemOrSameApp(pkg);
2573 mHandler.post(new Runnable() {
2576 mConditionProviders.notifyConditions(pkg, info, conditions);
2582 public void requestUnbindProvider(IConditionProvider provider) {
2583 long identity = Binder.clearCallingIdentity();
2585 // allow bound services to disable themselves
2586 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2587 info.getOwner().setComponentState(info.component, false);
2589 Binder.restoreCallingIdentity(identity);
2594 public void requestBindProvider(ComponentName component) {
2595 checkCallerIsSystemOrSameApp(component.getPackageName());
2596 long identity = Binder.clearCallingIdentity();
2598 mConditionProviders.setComponentState(component, true);
2600 Binder.restoreCallingIdentity(identity);
2604 private void enforceSystemOrSystemUI(String message) {
2605 if (isCallerSystemOrPhone()) return;
2606 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2610 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2612 checkCallerIsSystemOrSameApp(pkg);
2613 } catch (SecurityException e) {
2614 getContext().enforceCallingPermission(
2615 android.Manifest.permission.STATUS_BAR_SERVICE,
2620 private void enforcePolicyAccess(int uid, String method) {
2621 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2622 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2625 boolean accessAllowed = false;
2626 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2627 final int packageCount = packages.length;
2628 for (int i = 0; i < packageCount; i++) {
2629 if (mConditionProviders.isPackageOrComponentAllowed(
2630 packages[i], UserHandle.getUserId(uid))) {
2631 accessAllowed = true;
2634 if (!accessAllowed) {
2635 Slog.w(TAG, "Notification policy access denied calling " + method);
2636 throw new SecurityException("Notification policy access denied");
2640 private void enforcePolicyAccess(String pkg, String method) {
2641 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2642 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2645 checkCallerIsSameApp(pkg);
2646 if (!checkPolicyAccess(pkg)) {
2647 Slog.w(TAG, "Notification policy access denied calling " + method);
2648 throw new SecurityException("Notification policy access denied");
2652 private boolean checkPackagePolicyAccess(String pkg) {
2653 return mConditionProviders.isPackageOrComponentAllowed(
2654 pkg, getCallingUserHandle().getIdentifier());
2657 private boolean checkPolicyAccess(String pkg) {
2659 int uid = getContext().getPackageManager().getPackageUidAsUser(
2660 pkg, UserHandle.getCallingUserId());
2661 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2662 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2666 } catch (NameNotFoundException e) {
2669 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2673 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2674 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2675 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2676 if (filter != null && filter.stats) {
2677 dumpJson(pw, filter);
2678 } else if (filter != null && filter.proto) {
2679 dumpProto(fd, filter);
2681 dumpImpl(pw, filter);
2686 public ComponentName getEffectsSuppressor() {
2687 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2691 public boolean matchesCallFilter(Bundle extras) {
2692 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2693 return mZenModeHelper.matchesCallFilter(
2694 Binder.getCallingUserHandle(),
2696 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2697 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2698 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2702 public boolean isSystemConditionProviderEnabled(String path) {
2703 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2704 return mConditionProviders.isSystemProviderEnabled(path);
2707 // Backup/restore interface
2709 public byte[] getBackupPayload(int user) {
2710 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2711 //TODO: http://b/22388012
2712 if (user != UserHandle.USER_SYSTEM) {
2713 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2716 synchronized(mPolicyFile) {
2717 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2719 writePolicyXml(baos, true /*forBackup*/);
2720 return baos.toByteArray();
2721 } catch (IOException e) {
2722 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2729 public void applyRestore(byte[] payload, int user) {
2730 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2731 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2732 if (payload == null) {
2733 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2736 //TODO: http://b/22388012
2737 if (user != UserHandle.USER_SYSTEM) {
2738 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2741 synchronized(mPolicyFile) {
2742 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2744 readPolicyXml(bais, true /*forRestore*/);
2746 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2747 Slog.w(TAG, "applyRestore: error reading payload", e);
2753 public boolean isNotificationPolicyAccessGranted(String pkg) {
2754 return checkPolicyAccess(pkg);
2758 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2759 enforceSystemOrSystemUIOrSamePackage(pkg,
2760 "request policy access status for another package");
2761 return checkPolicyAccess(pkg);
2765 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2766 throws RemoteException {
2767 checkCallerIsSystemOrShell();
2768 final long identity = Binder.clearCallingIdentity();
2770 if (!mActivityManager.isLowRamDevice()) {
2771 mConditionProviders.setPackageOrComponentEnabled(
2772 pkg, getCallingUserHandle().getIdentifier(), true, granted);
2774 getContext().sendBroadcastAsUser(new Intent(
2775 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2777 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2778 getCallingUserHandle(), null);
2783 Binder.restoreCallingIdentity(identity);
2788 public Policy getNotificationPolicy(String pkg) {
2789 enforcePolicyAccess(pkg, "getNotificationPolicy");
2790 final long identity = Binder.clearCallingIdentity();
2792 return mZenModeHelper.getNotificationPolicy();
2794 Binder.restoreCallingIdentity(identity);
2799 public void setNotificationPolicy(String pkg, Policy policy) {
2800 enforcePolicyAccess(pkg, "setNotificationPolicy");
2801 final long identity = Binder.clearCallingIdentity();
2803 mZenModeHelper.setNotificationPolicy(policy);
2805 Binder.restoreCallingIdentity(identity);
2810 public List<String> getEnabledNotificationListenerPackages() {
2811 checkCallerIsSystem();
2812 return mListeners.getAllowedPackages(getCallingUserHandle().getIdentifier());
2816 public List<ComponentName> getEnabledNotificationListeners(int userId) {
2817 checkCallerIsSystem();
2818 return mListeners.getAllowedComponents(userId);
2822 public boolean isNotificationListenerAccessGranted(ComponentName listener) {
2823 Preconditions.checkNotNull(listener);
2824 checkCallerIsSystemOrSameApp(listener.getPackageName());
2825 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2826 getCallingUserHandle().getIdentifier());
2830 public boolean isNotificationListenerAccessGrantedForUser(ComponentName listener,
2832 Preconditions.checkNotNull(listener);
2833 checkCallerIsSystem();
2834 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2839 public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
2840 Preconditions.checkNotNull(assistant);
2841 checkCallerIsSystemOrSameApp(assistant.getPackageName());
2842 return mAssistants.isPackageOrComponentAllowed(assistant.flattenToString(),
2843 getCallingUserHandle().getIdentifier());
2847 public void setNotificationListenerAccessGranted(ComponentName listener,
2848 boolean granted) throws RemoteException {
2849 setNotificationListenerAccessGrantedForUser(
2850 listener, getCallingUserHandle().getIdentifier(), granted);
2854 public void setNotificationAssistantAccessGranted(ComponentName assistant,
2855 boolean granted) throws RemoteException {
2856 setNotificationAssistantAccessGrantedForUser(
2857 assistant, getCallingUserHandle().getIdentifier(), granted);
2861 public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
2862 boolean granted) throws RemoteException {
2863 Preconditions.checkNotNull(listener);
2864 checkCallerIsSystemOrShell();
2865 final long identity = Binder.clearCallingIdentity();
2867 if (!mActivityManager.isLowRamDevice()) {
2868 mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
2869 userId, false, granted);
2870 mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
2871 userId, true, granted);
2873 getContext().sendBroadcastAsUser(new Intent(
2874 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2876 .setPackage(listener.getPackageName())
2877 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2878 getCallingUserHandle(), null);
2883 Binder.restoreCallingIdentity(identity);
2888 public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
2889 int userId, boolean granted) throws RemoteException {
2890 Preconditions.checkNotNull(assistant);
2891 checkCallerIsSystemOrShell();
2892 final long identity = Binder.clearCallingIdentity();
2894 if (!mActivityManager.isLowRamDevice()) {
2895 mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
2896 userId, false, granted);
2897 mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
2898 userId, true, granted);
2900 getContext().sendBroadcastAsUser(new Intent(
2901 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2902 .setPackage(assistant.getPackageName())
2903 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2904 getCallingUserHandle(), null);
2909 Binder.restoreCallingIdentity(identity);
2914 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2915 Adjustment adjustment) throws RemoteException {
2916 final long identity = Binder.clearCallingIdentity();
2918 synchronized (mNotificationLock) {
2919 mAssistants.checkServiceTokenLocked(token);
2920 int N = mEnqueuedNotifications.size();
2921 for (int i = 0; i < N; i++) {
2922 final NotificationRecord n = mEnqueuedNotifications.get(i);
2923 if (Objects.equals(adjustment.getKey(), n.getKey())
2924 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2925 applyAdjustment(n, adjustment);
2931 Binder.restoreCallingIdentity(identity);
2936 public void applyAdjustmentFromAssistant(INotificationListener token,
2937 Adjustment adjustment) throws RemoteException {
2938 final long identity = Binder.clearCallingIdentity();
2940 synchronized (mNotificationLock) {
2941 mAssistants.checkServiceTokenLocked(token);
2942 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2943 applyAdjustment(n, adjustment);
2945 mRankingHandler.requestSort();
2947 Binder.restoreCallingIdentity(identity);
2952 public void applyAdjustmentsFromAssistant(INotificationListener token,
2953 List<Adjustment> adjustments) throws RemoteException {
2955 final long identity = Binder.clearCallingIdentity();
2957 synchronized (mNotificationLock) {
2958 mAssistants.checkServiceTokenLocked(token);
2959 for (Adjustment adjustment : adjustments) {
2960 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2961 applyAdjustment(n, adjustment);
2964 mRankingHandler.requestSort();
2966 Binder.restoreCallingIdentity(identity);
2971 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2972 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2973 Preconditions.checkNotNull(channel);
2974 Preconditions.checkNotNull(pkg);
2975 Preconditions.checkNotNull(user);
2977 verifyPrivilegedListener(token, user);
2978 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2982 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2983 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2984 Preconditions.checkNotNull(pkg);
2985 Preconditions.checkNotNull(user);
2986 verifyPrivilegedListener(token, user);
2988 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2989 false /* includeDeleted */);
2993 public ParceledListSlice<NotificationChannelGroup>
2994 getNotificationChannelGroupsFromPrivilegedListener(
2995 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2996 Preconditions.checkNotNull(pkg);
2997 Preconditions.checkNotNull(user);
2998 verifyPrivilegedListener(token, user);
3000 List<NotificationChannelGroup> groups = new ArrayList<>();
3001 groups.addAll(mRankingHelper.getNotificationChannelGroups(
3002 pkg, getUidForPackageAndUser(pkg, user)));
3003 return new ParceledListSlice<>(groups);
3006 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
3007 ManagedServiceInfo info;
3008 synchronized (mNotificationLock) {
3009 info = mListeners.checkServiceTokenLocked(token);
3011 if (!hasCompanionDevice(info)) {
3012 throw new SecurityException(info + " does not have access");
3014 if (!info.enabledAndUserMatches(user.getIdentifier())) {
3015 throw new SecurityException(info + " does not have access");
3019 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
3021 long identity = Binder.clearCallingIdentity();
3023 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
3025 Binder.restoreCallingIdentity(identity);
3031 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
3032 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
3033 throws RemoteException {
3034 new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
3038 private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
3042 if (adjustment.getSignals() != null) {
3043 Bundle.setDefusable(adjustment.getSignals(), true);
3044 r.addAdjustment(adjustment);
3048 @GuardedBy("mNotificationLock")
3049 void addAutogroupKeyLocked(String key) {
3050 NotificationRecord r = mNotificationsByKey.get(key);
3054 if (r.sbn.getOverrideGroupKey() == null) {
3055 addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
3056 EventLogTags.writeNotificationAutogrouped(key);
3057 mRankingHandler.requestSort();
3061 @GuardedBy("mNotificationLock")
3062 void removeAutogroupKeyLocked(String key) {
3063 NotificationRecord r = mNotificationsByKey.get(key);
3067 if (r.sbn.getOverrideGroupKey() != null) {
3068 addAutoGroupAdjustment(r, null);
3069 EventLogTags.writeNotificationUnautogrouped(key);
3070 mRankingHandler.requestSort();
3074 private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
3075 Bundle signals = new Bundle();
3076 signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
3077 Adjustment adjustment =
3078 new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
3079 r.addAdjustment(adjustment);
3082 // Clears the 'fake' auto-group summary.
3083 @GuardedBy("mNotificationLock")
3084 private void clearAutogroupSummaryLocked(int userId, String pkg) {
3085 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3086 if (summaries != null && summaries.containsKey(pkg)) {
3088 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
3089 if (removed != null) {
3090 boolean wasPosted = removeFromNotificationListsLocked(removed);
3091 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted, null);
3096 @GuardedBy("mNotificationLock")
3097 private boolean hasAutoGroupSummaryLocked(StatusBarNotification sbn) {
3098 ArrayMap<String, String> summaries = mAutobundledSummaries.get(sbn.getUserId());
3099 return summaries != null && summaries.containsKey(sbn.getPackageName());
3102 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
3103 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
3104 NotificationRecord summaryRecord = null;
3105 synchronized (mNotificationLock) {
3106 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
3107 if (notificationRecord == null) {
3108 // The notification could have been cancelled again already. A successive
3109 // adjustment will post a summary if needed.
3112 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
3113 userId = adjustedSbn.getUser().getIdentifier();
3114 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3115 if (summaries == null) {
3116 summaries = new ArrayMap<>();
3118 mAutobundledSummaries.put(userId, summaries);
3119 if (!summaries.containsKey(pkg)) {
3121 final ApplicationInfo appInfo =
3122 adjustedSbn.getNotification().extras.getParcelable(
3123 Notification.EXTRA_BUILDER_APPLICATION_INFO);
3124 final Bundle extras = new Bundle();
3125 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
3126 final String channelId = notificationRecord.getChannel().getId();
3127 final Notification summaryNotification =
3128 new Notification.Builder(getContext(), channelId)
3129 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
3130 .setGroupSummary(true)
3131 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
3132 .setGroup(GroupHelper.AUTOGROUP_KEY)
3133 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
3134 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
3135 .setColor(adjustedSbn.getNotification().color)
3138 summaryNotification.extras.putAll(extras);
3139 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
3140 if (appIntent != null) {
3141 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
3142 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
3144 final StatusBarNotification summarySbn =
3145 new StatusBarNotification(adjustedSbn.getPackageName(),
3146 adjustedSbn.getOpPkg(),
3148 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
3149 adjustedSbn.getInitialPid(), summaryNotification,
3150 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
3151 System.currentTimeMillis());
3152 summaryRecord = new NotificationRecord(getContext(), summarySbn,
3153 notificationRecord.getChannel());
3154 summaries.put(pkg, summarySbn.getKey());
3157 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
3158 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord, true)) {
3159 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
3163 private String disableNotificationEffects(NotificationRecord record) {
3164 if (mDisableNotificationEffects) {
3165 return "booleanState";
3167 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
3168 return "listenerHints";
3170 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
3176 private void dumpJson(PrintWriter pw, DumpFilter filter) {
3177 JSONObject dump = new JSONObject();
3179 dump.put("service", "Notification Manager");
3180 dump.put("bans", mRankingHelper.dumpBansJson(filter));
3181 dump.put("ranking", mRankingHelper.dumpJson(filter));
3182 dump.put("stats", mUsageStats.dumpJson(filter));
3183 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
3184 } catch (JSONException e) {
3185 e.printStackTrace();
3190 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
3191 final ProtoOutputStream proto = new ProtoOutputStream(fd);
3192 synchronized (mNotificationLock) {
3193 long records = proto.start(NotificationServiceDumpProto.RECORDS);
3194 int N = mNotificationList.size();
3196 for (int i = 0; i < N; i++) {
3197 final NotificationRecord nr = mNotificationList.get(i);
3198 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3199 nr.dump(proto, filter.redact);
3200 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
3203 N = mEnqueuedNotifications.size();
3205 for (int i = 0; i < N; i++) {
3206 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3207 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3208 nr.dump(proto, filter.redact);
3209 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
3212 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
3215 for (int i = 0; i < N; i++) {
3216 final NotificationRecord nr = snoozed.get(i);
3217 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3218 nr.dump(proto, filter.redact);
3219 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
3225 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
3226 mZenModeHelper.dump(proto);
3227 for (ComponentName suppressor : mEffectsSuppressors) {
3228 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
3235 void dumpImpl(PrintWriter pw, DumpFilter filter) {
3236 pw.print("Current Notification Manager state");
3237 if (filter.filtered) {
3238 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
3242 final boolean zenOnly = filter.filtered && filter.zen;
3245 synchronized (mToastQueue) {
3246 N = mToastQueue.size();
3248 pw.println(" Toast Queue:");
3249 for (int i=0; i<N; i++) {
3250 mToastQueue.get(i).dump(pw, " ", filter);
3257 synchronized (mNotificationLock) {
3259 N = mNotificationList.size();
3261 pw.println(" Notification List:");
3262 for (int i=0; i<N; i++) {
3263 final NotificationRecord nr = mNotificationList.get(i);
3264 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3265 nr.dump(pw, " ", getContext(), filter.redact);
3270 if (!filter.filtered) {
3273 pw.println(" Lights List:");
3274 for (int i=0; i<N; i++) {
3280 pw.println(mLights.get(i));
3284 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3285 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
3286 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3287 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
3288 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
3289 pw.println(" mCallState=" + callStateToString(mCallState));
3290 pw.println(" mSystemReady=" + mSystemReady);
3291 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
3293 pw.println(" mArchive=" + mArchive.toString());
3294 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
3296 while (iter.hasNext()) {
3297 final StatusBarNotification sbn = iter.next();
3298 if (filter != null && !filter.matches(sbn)) continue;
3299 pw.println(" " + sbn);
3301 if (iter.hasNext()) pw.println(" ...");
3307 N = mEnqueuedNotifications.size();
3309 pw.println(" Enqueued Notification List:");
3310 for (int i = 0; i < N; i++) {
3311 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3312 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3313 nr.dump(pw, " ", getContext(), filter.redact);
3318 mSnoozeHelper.dump(pw, filter);
3323 pw.println("\n Ranking Config:");
3324 mRankingHelper.dump(pw, " ", filter);
3326 pw.println("\n Notification listeners:");
3327 mListeners.dump(pw, filter);
3328 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3329 pw.print(" mListenersDisablingEffects: (");
3330 N = mListenersDisablingEffects.size();
3331 for (int i = 0; i < N; i++) {
3332 final int hint = mListenersDisablingEffects.keyAt(i);
3333 if (i > 0) pw.print(';');
3334 pw.print("hint[" + hint + "]:");
3336 final ArraySet<ManagedServiceInfo> listeners =
3337 mListenersDisablingEffects.valueAt(i);
3338 final int listenerSize = listeners.size();
3340 for (int j = 0; j < listenerSize; j++) {
3341 if (i > 0) pw.print(',');
3342 final ManagedServiceInfo listener = listeners.valueAt(i);
3343 pw.print(listener.component);
3347 pw.println("\n Notification assistant services:");
3348 mAssistants.dump(pw, filter);
3351 if (!filter.filtered || zenOnly) {
3352 pw.println("\n Zen Mode:");
3353 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3354 mZenModeHelper.dump(pw, " ");
3356 pw.println("\n Zen Log:");
3357 ZenLog.dump(pw, " ");
3360 pw.println("\n Condition providers:");
3361 mConditionProviders.dump(pw, filter);
3363 pw.println("\n Group summaries:");
3364 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3365 NotificationRecord r = entry.getValue();
3366 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3367 if (mNotificationsByKey.get(r.getKey()) != r) {
3368 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3369 r.dump(pw, " ", getContext(), filter.redact);
3374 pw.println("\n Usage Stats:");
3375 mUsageStats.dump(pw, " ", filter);
3381 * The private API only accessible to the system process.
3383 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3385 public NotificationChannel getNotificationChannel(String pkg, int uid, String
3387 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
3391 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3392 String tag, int id, Notification notification, int userId) {
3393 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3398 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3400 checkCallerIsSystem();
3401 mHandler.post(new Runnable() {
3404 synchronized (mNotificationLock) {
3405 removeForegroundServiceFlagByListLocked(
3406 mEnqueuedNotifications, pkg, notificationId, userId);
3407 removeForegroundServiceFlagByListLocked(
3408 mNotificationList, pkg, notificationId, userId);
3414 @GuardedBy("mNotificationLock")
3415 private void removeForegroundServiceFlagByListLocked(
3416 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3418 NotificationRecord r = findNotificationByListLocked(
3419 notificationList, pkg, null, notificationId, userId);
3423 StatusBarNotification sbn = r.sbn;
3424 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3425 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3426 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3427 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3428 sbn.getNotification().flags =
3429 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3430 mRankingHelper.sort(mNotificationList);
3431 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3435 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3436 final int callingPid, final String tag, final int id, final Notification notification,
3437 int incomingUserId) {
3439 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3440 + " notification=" + notification);
3442 checkCallerIsSystemOrSameApp(pkg);
3444 final int userId = ActivityManager.handleIncomingUser(callingPid,
3445 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3446 final UserHandle user = new UserHandle(userId);
3448 if (pkg == null || notification == null) {
3449 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3450 + " id=" + id + " notification=" + notification);
3453 // The system can post notifications for any package, let us resolve that.
3454 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3456 // Fix the notification as best we can.
3458 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3459 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3460 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3461 Notification.addFieldsFromContext(ai, notification);
3463 int canColorize = mPackageManagerClient.checkPermission(
3464 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
3465 if (canColorize == PERMISSION_GRANTED) {
3466 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3468 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3471 } catch (NameNotFoundException e) {
3472 Slog.e(TAG, "Cannot create a context for sending app", e);
3476 mUsageStats.registerEnqueuedByApp(pkg);
3478 // setup local book-keeping
3479 String channelId = notification.getChannelId();
3480 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3481 channelId = (new Notification.TvExtender(notification)).getChannelId();
3483 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3484 notificationUid, channelId, false /* includeDeleted */);
3485 if (channel == null) {
3486 final String noChannelStr = "No Channel found for "
3488 + ", channelId=" + channelId
3491 + ", opPkg=" + opPkg
3492 + ", callingUid=" + callingUid
3493 + ", userId=" + userId
3494 + ", incomingUserId=" + incomingUserId
3495 + ", notificationUid=" + notificationUid
3496 + ", notification=" + notification;
3497 Log.e(TAG, noChannelStr);
3498 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3499 "Failed to post notification on channel \"" + channelId + "\"\n" +
3500 "See log for more details");
3504 final StatusBarNotification n = new StatusBarNotification(
3505 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3506 user, null, System.currentTimeMillis());
3507 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3509 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
3510 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
3511 && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
3512 // Increase the importance of foreground service notifications unless the user had an
3513 // opinion otherwise
3514 if (TextUtils.isEmpty(channelId)
3515 || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
3516 r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service");
3518 channel.setImportance(IMPORTANCE_LOW);
3519 mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
3520 r.updateNotificationChannel(channel);
3524 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
3525 r.sbn.getOverrideGroupKey() != null)) {
3529 // Whitelist pending intents.
3530 if (notification.allPendingIntents != null) {
3531 final int intentCount = notification.allPendingIntents.size();
3532 if (intentCount > 0) {
3533 final ActivityManagerInternal am = LocalServices
3534 .getService(ActivityManagerInternal.class);
3535 final long duration = LocalServices.getService(
3536 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3537 for (int i = 0; i < intentCount; i++) {
3538 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3539 if (pendingIntent != null) {
3540 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3541 WHITELIST_TOKEN, duration);
3547 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3550 private void doChannelWarningToast(CharSequence toastText) {
3551 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3552 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3553 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3554 if (warningEnabled) {
3555 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3556 Toast.LENGTH_SHORT);
3561 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3562 // The system can post notifications on behalf of any package it wants
3563 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3565 return getContext().getPackageManager()
3566 .getPackageUidAsUser(opPackageName, userId);
3567 } catch (NameNotFoundException e) {
3575 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3579 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3580 NotificationRecord r, boolean isAutogroup) {
3581 final String pkg = r.sbn.getPackageName();
3582 final String dialerPackage =
3583 getContext().getSystemService(TelecomManager.class).getSystemDialerPackage();
3584 final boolean isSystemNotification =
3585 isUidSystemOrPhone(callingUid) || ("android".equals(pkg))
3586 || TextUtils.equals(pkg, dialerPackage);
3587 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3589 // Limit the number of notifications that any given package except the android
3590 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3591 if (!isSystemNotification && !isNotificationFromListener) {
3592 synchronized (mNotificationLock) {
3593 if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
3594 // Ephemeral apps have some special constraints for notifications.
3595 // They are not allowed to create new notifications however they are allowed to
3596 // update notifications created by the system (e.g. a foreground service
3598 throw new SecurityException("Instant app " + pkg
3599 + " cannot create notifications");
3602 // rate limit updates that aren't completed progress notifications
3603 if (mNotificationsByKey.get(r.sbn.getKey()) != null
3604 && !r.getNotification().hasCompletedProgress()
3607 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3608 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3609 mUsageStats.registerOverRateQuota(pkg);
3610 final long now = SystemClock.elapsedRealtime();
3611 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3612 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3613 + ". Shedding " + r.sbn.getKey() + ". package=" + pkg);
3614 mLastOverRateLogTime = now;
3620 // limit the number of outstanding notificationrecords an app can have
3621 int count = getNotificationCountLocked(pkg, userId, id, tag);
3622 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3623 mUsageStats.registerOverCountQuota(pkg);
3624 Slog.e(TAG, "Package has already posted or enqueued " + count
3625 + " notifications. Not showing more. package=" + pkg);
3632 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3633 MetricsLogger.action(r.getLogMaker()
3634 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3635 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3637 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3639 mSnoozeHelper.update(userId, r);
3646 if (isBlocked(r, mUsageStats)) {
3653 protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
3654 String excludedTag) {
3656 final int N = mNotificationList.size();
3657 for (int i = 0; i < N; i++) {
3658 final NotificationRecord existing = mNotificationList.get(i);
3659 if (existing.sbn.getPackageName().equals(pkg)
3660 && existing.sbn.getUserId() == userId) {
3661 if (existing.sbn.getId() == excludedId
3662 && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
3668 final int M = mEnqueuedNotifications.size();
3669 for (int i = 0; i < M; i++) {
3670 final NotificationRecord existing = mEnqueuedNotifications.get(i);
3671 if (existing.sbn.getPackageName().equals(pkg)
3672 && existing.sbn.getUserId() == userId) {
3679 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3680 final String pkg = r.sbn.getPackageName();
3681 final int callingUid = r.sbn.getUid();
3683 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3684 if (isPackageSuspended) {
3685 Slog.e(TAG, "Suppressing notification from package due to package "
3686 + "suspended by administrator.");
3687 usageStats.registerSuspendedByAdmin(r);
3688 return isPackageSuspended;
3691 final boolean isBlocked =
3692 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
3693 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3695 Slog.e(TAG, "Suppressing notification from package by user request.");
3696 usageStats.registerBlocked(r);
3701 protected class SnoozeNotificationRunnable implements Runnable {
3702 private final String mKey;
3703 private final long mDuration;
3704 private final String mSnoozeCriterionId;
3706 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3708 mDuration = duration;
3709 mSnoozeCriterionId = snoozeCriterionId;
3714 synchronized (mNotificationLock) {
3715 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3722 @GuardedBy("mNotificationLock")
3723 void snoozeLocked(NotificationRecord r) {
3724 if (r.sbn.isGroup()) {
3725 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3726 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3727 if (r.getNotification().isGroupSummary()) {
3728 // snooze summary and all children
3729 for (int i = 0; i < groupNotifications.size(); i++) {
3730 snoozeNotificationLocked(groupNotifications.get(i));
3733 // if there is a valid summary for this group, and we are snoozing the only
3734 // child, also snooze the summary
3735 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3736 if (groupNotifications.size() != 2) {
3737 snoozeNotificationLocked(r);
3739 // snooze summary and the one child
3740 for (int i = 0; i < groupNotifications.size(); i++) {
3741 snoozeNotificationLocked(groupNotifications.get(i));
3745 snoozeNotificationLocked(r);
3749 // just snooze the one notification
3750 snoozeNotificationLocked(r);
3754 @GuardedBy("mNotificationLock")
3755 void snoozeNotificationLocked(NotificationRecord r) {
3756 MetricsLogger.action(r.getLogMaker()
3757 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3758 .setType(MetricsEvent.TYPE_CLOSE)
3759 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS,
3761 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3762 mSnoozeCriterionId == null ? 0 : 1));
3763 boolean wasPosted = removeFromNotificationListsLocked(r);
3764 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
3765 updateLightsLocked();
3766 if (mSnoozeCriterionId != null) {
3767 mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3768 mSnoozeHelper.snooze(r);
3770 mSnoozeHelper.snooze(r, mDuration);
3776 protected class EnqueueNotificationRunnable implements Runnable {
3777 private final NotificationRecord r;
3778 private final int userId;
3780 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3781 this.userId = userId;
3787 synchronized (mNotificationLock) {
3788 mEnqueuedNotifications.add(r);
3789 scheduleTimeoutLocked(r);
3791 final StatusBarNotification n = r.sbn;
3792 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3793 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3795 // Retain ranking information from previous record
3796 r.copyRankingInformation(old);
3799 final int callingUid = n.getUid();
3800 final int callingPid = n.getInitialPid();
3801 final Notification notification = n.getNotification();
3802 final String pkg = n.getPackageName();
3803 final int id = n.getId();
3804 final String tag = n.getTag();
3806 // Handle grouped notifications and bail out early if we
3807 // can to avoid extracting signals.
3808 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3810 // if this is a group child, unsnooze parent summary
3811 if (n.isGroup() && notification.isGroupChild()) {
3812 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3815 // This conditional is a dirty hack to limit the logging done on
3816 // behalf of the download manager without affecting other apps.
3817 if (!pkg.equals("com.android.providers.downloads")
3818 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3819 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3821 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3823 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3824 pkg, id, tag, userId, notification.toString(),
3828 mRankingHelper.extractSignals(r);
3830 // tell the assistant service about the notification
3831 if (mAssistants.isEnabled()) {
3832 mAssistants.onNotificationEnqueued(r);
3833 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3834 DELAY_FOR_ASSISTANT_TIME);
3836 mHandler.post(new PostNotificationRunnable(r.getKey()));
3842 protected class PostNotificationRunnable implements Runnable {
3843 private final String key;
3845 PostNotificationRunnable(String key) {
3851 synchronized (mNotificationLock) {
3853 NotificationRecord r = null;
3854 int N = mEnqueuedNotifications.size();
3855 for (int i = 0; i < N; i++) {
3856 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3857 if (Objects.equals(key, enqueued.getKey())) {
3863 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3866 NotificationRecord old = mNotificationsByKey.get(key);
3867 final StatusBarNotification n = r.sbn;
3868 final Notification notification = n.getNotification();
3869 int index = indexOfNotificationLocked(n.getKey());
3871 mNotificationList.add(r);
3872 mUsageStats.registerPostedByApp(r);
3874 old = mNotificationList.get(index);
3875 mNotificationList.set(index, r);
3876 mUsageStats.registerUpdatedByApp(r, old);
3877 // Make sure we don't lose the foreground service state.
3878 notification.flags |=
3879 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3883 mNotificationsByKey.put(n.getKey(), r);
3885 // Ensure if this is a foreground service that the proper additional
3887 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3888 notification.flags |= Notification.FLAG_ONGOING_EVENT
3889 | Notification.FLAG_NO_CLEAR;
3892 applyZenModeLocked(r);
3893 mRankingHelper.sort(mNotificationList);
3895 if (notification.getSmallIcon() != null) {
3896 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3897 mListeners.notifyPostedLocked(n, oldSbn);
3898 if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
3899 mHandler.post(new Runnable() {
3902 mGroupHelper.onNotificationPosted(
3903 n, hasAutoGroupSummaryLocked(n));
3908 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3909 if (old != null && !old.isCanceled) {
3910 mListeners.notifyRemovedLocked(n,
3911 NotificationListenerService.REASON_ERROR);
3912 mHandler.post(new Runnable() {
3915 mGroupHelper.onNotificationRemoved(n);
3919 // ATTENTION: in a future release we will bail out here
3920 // so that we do not play sounds, show lights, etc. for invalid
3922 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3923 + n.getPackageName());
3926 buzzBeepBlinkLocked(r);
3928 int N = mEnqueuedNotifications.size();
3929 for (int i = 0; i < N; i++) {
3930 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3931 if (Objects.equals(key, enqueued.getKey())) {
3932 mEnqueuedNotifications.remove(i);
3942 * Ensures that grouped notification receive their special treatment.
3944 * <p>Cancels group children if the new notification causes a group to lose
3947 * <p>Updates mSummaryByGroupKey.</p>
3949 @GuardedBy("mNotificationLock")
3950 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3951 int callingUid, int callingPid) {
3952 StatusBarNotification sbn = r.sbn;
3953 Notification n = sbn.getNotification();
3954 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3955 // notifications without a group shouldn't be a summary, otherwise autobundling can
3957 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3960 String group = sbn.getGroupKey();
3961 boolean isSummary = n.isGroupSummary();
3963 Notification oldN = old != null ? old.sbn.getNotification() : null;
3964 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3965 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3968 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3969 if (removedSummary != old) {
3971 removedSummary != null ? removedSummary.getKey() : "<null>";
3972 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3973 ", removed=" + removedKey);
3977 mSummaryByGroupKey.put(group, r);
3980 // Clear out group children of the old notification if the update
3981 // causes the group summary to go away. This happens when the old
3982 // notification was a summary and the new one isn't, or when the old
3983 // notification was a summary and its group key changed.
3984 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3985 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
3991 @GuardedBy("mNotificationLock")
3992 void scheduleTimeoutLocked(NotificationRecord record) {
3993 if (record.getNotification().getTimeoutAfter() > 0) {
3994 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3995 REQUEST_CODE_TIMEOUT,
3996 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3997 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3998 .appendPath(record.getKey()).build())
3999 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
4000 .putExtra(EXTRA_KEY, record.getKey()),
4001 PendingIntent.FLAG_UPDATE_CURRENT);
4002 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
4003 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
4008 @GuardedBy("mNotificationLock")
4009 void buzzBeepBlinkLocked(NotificationRecord record) {
4010 boolean buzz = false;
4011 boolean beep = false;
4012 boolean blink = false;
4014 final Notification notification = record.sbn.getNotification();
4015 final String key = record.getKey();
4017 // Should this notification make noise, vibe, or use the LED?
4018 final boolean aboveThreshold =
4019 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
4021 // Remember if this notification already owns the notification channels.
4022 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
4023 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
4024 // These are set inside the conditional if the notification is allowed to make noise.
4025 boolean hasValidVibrate = false;
4026 boolean hasValidSound = false;
4027 boolean sentAccessibilityEvent = false;
4028 // If the notification will appear in the status bar, it should send an accessibility
4030 if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
4031 sendAccessibilityEvent(notification, record.sbn.getPackageName());
4032 sentAccessibilityEvent = true;
4035 if (aboveThreshold && isNotificationForCurrentUser(record)) {
4037 if (mSystemReady && mAudioManager != null) {
4038 Uri soundUri = record.getSound();
4039 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
4040 long[] vibration = record.getVibration();
4041 // Demote sound to vibration if vibration missing & phone in vibration mode.
4042 if (vibration == null
4044 && (mAudioManager.getRingerModeInternal()
4045 == AudioManager.RINGER_MODE_VIBRATE)
4046 && mAudioManager.getStreamVolume(
4047 AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
4048 vibration = mFallbackVibrationPattern;
4050 hasValidVibrate = vibration != null;
4052 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
4053 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
4054 if (!sentAccessibilityEvent) {
4055 sendAccessibilityEvent(notification, record.sbn.getPackageName());
4056 sentAccessibilityEvent = true;
4058 if (DBG) Slog.v(TAG, "Interrupting!");
4059 if (hasValidSound) {
4060 mSoundNotificationKey = key;
4062 playInCallNotification();
4065 beep = playSound(record, soundUri);
4069 final boolean ringerModeSilent =
4070 mAudioManager.getRingerModeInternal()
4071 == AudioManager.RINGER_MODE_SILENT;
4072 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
4073 mVibrateNotificationKey = key;
4075 buzz = playVibration(record, vibration, hasValidSound);
4080 // If a notification is updated to remove the actively playing sound or vibrate,
4081 // cancel that feedback now
4082 if (wasBeep && !hasValidSound) {
4085 if (wasBuzz && !hasValidVibrate) {
4086 clearVibrateLocked();
4090 // release the light
4091 boolean wasShowLights = mLights.remove(key);
4092 if (record.getLight() != null && aboveThreshold
4093 && ((record.getSuppressedVisualEffects()
4094 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
4096 updateLightsLocked();
4097 if (mUseAttentionLight) {
4098 mAttentionLight.pulse();
4101 } else if (wasShowLights) {
4102 updateLightsLocked();
4104 if (buzz || beep || blink) {
4105 MetricsLogger.action(record.getLogMaker()
4106 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
4107 .setType(MetricsEvent.TYPE_OPEN)
4108 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
4109 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
4113 @GuardedBy("mNotificationLock")
4114 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
4115 // Suppressed because it's a silent update
4116 final Notification notification = record.getNotification();
4118 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
4122 // muted by listener
4123 final String disableEffects = disableNotificationEffects(record);
4124 if (disableEffects != null) {
4125 ZenLog.traceDisableEffects(record, disableEffects);
4129 // suppressed due to DND
4130 if (record.isIntercepted()) {
4134 // Suppressed because another notification in its group handles alerting
4135 if (record.sbn.isGroup()) {
4136 return notification.suppressAlertingDueToGrouping();
4139 // Suppressed for being too recently noisy
4140 final String pkg = record.sbn.getPackageName();
4141 if (mUsageStats.isAlertRateLimited(pkg)) {
4142 Slog.e(TAG, "Muting recently noisy " + record.getKey());
4149 private boolean playSound(final NotificationRecord record, Uri soundUri) {
4150 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4151 // do not play notifications if there is a user of exclusive audio focus
4152 // or the device is in vibrate mode
4153 if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
4154 != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
4155 AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
4156 final long identity = Binder.clearCallingIdentity();
4158 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4159 if (player != null) {
4160 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
4161 + " with attributes " + record.getAudioAttributes());
4162 player.playAsync(soundUri, record.sbn.getUser(), looping,
4163 record.getAudioAttributes());
4166 } catch (RemoteException e) {
4168 Binder.restoreCallingIdentity(identity);
4174 private boolean playVibration(final NotificationRecord record, long[] vibration,
4175 boolean delayVibForSound) {
4176 // Escalate privileges so we can use the vibrator even if the
4177 // notifying app does not have the VIBRATE permission.
4178 long identity = Binder.clearCallingIdentity();
4180 final VibrationEffect effect;
4182 final boolean insistent =
4183 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4184 effect = VibrationEffect.createWaveform(
4185 vibration, insistent ? 0 : -1 /*repeatIndex*/);
4186 } catch (IllegalArgumentException e) {
4187 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
4188 Arrays.toString(vibration));
4191 if (delayVibForSound) {
4193 // delay the vibration by the same amount as the notification sound
4194 final int waitMs = mAudioManager.getFocusRampTimeMs(
4195 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
4196 record.getAudioAttributes());
4197 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
4199 Thread.sleep(waitMs);
4200 } catch (InterruptedException e) { }
4201 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4202 effect, record.getAudioAttributes());
4205 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4206 effect, record.getAudioAttributes());
4210 Binder.restoreCallingIdentity(identity);
4214 private boolean isNotificationForCurrentUser(NotificationRecord record) {
4215 final int currentUser;
4216 final long token = Binder.clearCallingIdentity();
4218 currentUser = ActivityManager.getCurrentUser();
4220 Binder.restoreCallingIdentity(token);
4222 return (record.getUserId() == UserHandle.USER_ALL ||
4223 record.getUserId() == currentUser ||
4224 mUserProfiles.isCurrentProfile(record.getUserId()));
4227 protected void playInCallNotification() {
4231 final long identity = Binder.clearCallingIdentity();
4233 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4234 if (player != null) {
4235 player.play(new Binder(), mInCallNotificationUri,
4236 mInCallNotificationAudioAttributes,
4237 mInCallNotificationVolume, false);
4239 } catch (RemoteException e) {
4241 Binder.restoreCallingIdentity(identity);
4247 @GuardedBy("mToastQueue")
4248 void showNextToastLocked() {
4249 ToastRecord record = mToastQueue.get(0);
4250 while (record != null) {
4251 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
4253 record.callback.show(record.token);
4254 scheduleTimeoutLocked(record);
4256 } catch (RemoteException e) {
4257 Slog.w(TAG, "Object died trying to show notification " + record.callback
4258 + " in package " + record.pkg);
4259 // remove it from the list and let the process die
4260 int index = mToastQueue.indexOf(record);
4262 mToastQueue.remove(index);
4264 keepProcessAliveIfNeededLocked(record.pid);
4265 if (mToastQueue.size() > 0) {
4266 record = mToastQueue.get(0);
4274 @GuardedBy("mToastQueue")
4275 void cancelToastLocked(int index) {
4276 ToastRecord record = mToastQueue.get(index);
4278 record.callback.hide();
4279 } catch (RemoteException e) {
4280 Slog.w(TAG, "Object died trying to hide notification " + record.callback
4281 + " in package " + record.pkg);
4282 // don't worry about this, we're about to remove it from
4286 ToastRecord lastToast = mToastQueue.remove(index);
4287 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
4289 keepProcessAliveIfNeededLocked(record.pid);
4290 if (mToastQueue.size() > 0) {
4291 // Show the next one. If the callback fails, this will remove
4292 // it from the list, so don't assume that the list hasn't changed
4293 // after this point.
4294 showNextToastLocked();
4298 @GuardedBy("mToastQueue")
4299 private void scheduleTimeoutLocked(ToastRecord r)
4301 mHandler.removeCallbacksAndMessages(r);
4302 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
4303 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
4304 mHandler.sendMessageDelayed(m, delay);
4307 private void handleTimeout(ToastRecord record)
4309 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
4310 synchronized (mToastQueue) {
4311 int index = indexOfToastLocked(record.pkg, record.callback);
4313 cancelToastLocked(index);
4318 @GuardedBy("mToastQueue")
4319 int indexOfToastLocked(String pkg, ITransientNotification callback)
4321 IBinder cbak = callback.asBinder();
4322 ArrayList<ToastRecord> list = mToastQueue;
4323 int len = list.size();
4324 for (int i=0; i<len; i++) {
4325 ToastRecord r = list.get(i);
4326 if (r.pkg.equals(pkg) && r.callback.asBinder().equals(cbak)) {
4333 @GuardedBy("mToastQueue")
4334 int indexOfToastPackageLocked(String pkg)
4336 ArrayList<ToastRecord> list = mToastQueue;
4337 int len = list.size();
4338 for (int i=0; i<len; i++) {
4339 ToastRecord r = list.get(i);
4340 if (r.pkg.equals(pkg)) {
4347 @GuardedBy("mToastQueue")
4348 void keepProcessAliveIfNeededLocked(int pid)
4350 int toastCount = 0; // toasts from this pid
4351 ArrayList<ToastRecord> list = mToastQueue;
4352 int N = list.size();
4353 for (int i=0; i<N; i++) {
4354 ToastRecord r = list.get(i);
4360 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
4361 } catch (RemoteException e) {
4362 // Shouldn't happen.
4366 private void handleRankingReconsideration(Message message) {
4367 if (!(message.obj instanceof RankingReconsideration)) return;
4368 RankingReconsideration recon = (RankingReconsideration) message.obj;
4371 synchronized (mNotificationLock) {
4372 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4373 if (record == null) {
4376 int indexBefore = findNotificationRecordIndexLocked(record);
4377 boolean interceptBefore = record.isIntercepted();
4378 float contactAffinityBefore = record.getContactAffinity();
4379 int visibilityBefore = record.getPackageVisibilityOverride();
4380 recon.applyChangesLocked(record);
4381 applyZenModeLocked(record);
4382 mRankingHelper.sort(mNotificationList);
4383 int indexAfter = findNotificationRecordIndexLocked(record);
4384 boolean interceptAfter = record.isIntercepted();
4385 float contactAffinityAfter = record.getContactAffinity();
4386 int visibilityAfter = record.getPackageVisibilityOverride();
4387 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4388 || visibilityBefore != visibilityAfter;
4389 if (interceptBefore && !interceptAfter
4390 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
4391 buzzBeepBlinkLocked(record);
4395 mHandler.scheduleSendRankingUpdate();
4399 void handleRankingSort() {
4400 if (mRankingHelper == null) return;
4401 synchronized (mNotificationLock) {
4402 final int N = mNotificationList.size();
4403 // Any field that can change via one of the extractors needs to be added here.
4404 ArrayList<String> orderBefore = new ArrayList<>(N);
4405 int[] visibilities = new int[N];
4406 boolean[] showBadges = new boolean[N];
4407 ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
4408 ArrayList<String> groupKeyBefore = new ArrayList<>(N);
4409 ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
4410 ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
4411 for (int i = 0; i < N; i++) {
4412 final NotificationRecord r = mNotificationList.get(i);
4413 orderBefore.add(r.getKey());
4414 visibilities[i] = r.getPackageVisibilityOverride();
4415 showBadges[i] = r.canShowBadge();
4416 channelBefore.add(r.getChannel());
4417 groupKeyBefore.add(r.getGroupKey());
4418 overridePeopleBefore.add(r.getPeopleOverride());
4419 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
4420 mRankingHelper.extractSignals(r);
4422 mRankingHelper.sort(mNotificationList);
4423 for (int i = 0; i < N; i++) {
4424 final NotificationRecord r = mNotificationList.get(i);
4425 if (!orderBefore.get(i).equals(r.getKey())
4426 || visibilities[i] != r.getPackageVisibilityOverride()
4427 || showBadges[i] != r.canShowBadge()
4428 || !Objects.equals(channelBefore.get(i), r.getChannel())
4429 || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
4430 || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
4431 || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
4432 mHandler.scheduleSendRankingUpdate();
4439 @GuardedBy("mNotificationLock")
4440 private void recordCallerLocked(NotificationRecord record) {
4441 if (mZenModeHelper.isCall(record)) {
4442 mZenModeHelper.recordCaller(record);
4446 // let zen mode evaluate this record
4447 @GuardedBy("mNotificationLock")
4448 private void applyZenModeLocked(NotificationRecord record) {
4449 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4450 if (record.isIntercepted()) {
4451 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4452 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4453 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4454 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4455 record.setSuppressedVisualEffects(suppressed);
4457 record.setSuppressedVisualEffects(0);
4461 @GuardedBy("mNotificationLock")
4462 private int findNotificationRecordIndexLocked(NotificationRecord target) {
4463 return mRankingHelper.indexOf(mNotificationList, target);
4466 private void handleSendRankingUpdate() {
4467 synchronized (mNotificationLock) {
4468 mListeners.notifyRankingUpdateLocked();
4472 private void scheduleListenerHintsChanged(int state) {
4473 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4474 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4477 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4478 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4479 mHandler.obtainMessage(
4480 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4481 listenerInterruptionFilter,
4485 private void handleListenerHintsChanged(int hints) {
4486 synchronized (mNotificationLock) {
4487 mListeners.notifyListenerHintsChangedLocked(hints);
4491 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4492 synchronized (mNotificationLock) {
4493 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4497 protected class WorkerHandler extends Handler
4499 public WorkerHandler(Looper looper) {
4504 public void handleMessage(Message msg)
4508 case MESSAGE_TIMEOUT:
4509 handleTimeout((ToastRecord)msg.obj);
4511 case MESSAGE_SAVE_POLICY_FILE:
4512 handleSavePolicyFile();
4514 case MESSAGE_SEND_RANKING_UPDATE:
4515 handleSendRankingUpdate();
4517 case MESSAGE_LISTENER_HINTS_CHANGED:
4518 handleListenerHintsChanged(msg.arg1);
4520 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4521 handleListenerInterruptionFilterChanged(msg.arg1);
4526 protected void scheduleSendRankingUpdate() {
4527 if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4528 Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
4535 private final class RankingHandlerWorker extends Handler implements RankingHandler
4537 public RankingHandlerWorker(Looper looper) {
4542 public void handleMessage(Message msg) {
4544 case MESSAGE_RECONSIDER_RANKING:
4545 handleRankingReconsideration(msg);
4547 case MESSAGE_RANKING_SORT:
4548 handleRankingSort();
4553 public void requestSort() {
4554 removeMessages(MESSAGE_RANKING_SORT);
4555 Message msg = Message.obtain();
4556 msg.what = MESSAGE_RANKING_SORT;
4560 public void requestReconsideration(RankingReconsideration recon) {
4561 Message m = Message.obtain(this,
4562 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4563 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4564 sendMessageDelayed(m, delay);
4569 // ============================================================================
4570 static int clamp(int x, int low, int high) {
4571 return (x < low) ? low : ((x > high) ? high : x);
4574 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4575 if (!mAccessibilityManager.isEnabled()) {
4579 AccessibilityEvent event =
4580 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4581 event.setPackageName(packageName);
4582 event.setClassName(Notification.class.getName());
4583 event.setParcelableData(notification);
4584 CharSequence tickerText = notification.tickerText;
4585 if (!TextUtils.isEmpty(tickerText)) {
4586 event.getText().add(tickerText);
4589 mAccessibilityManager.sendAccessibilityEvent(event);
4593 * Removes all NotificationsRecords with the same key as the given notification record
4594 * from both lists. Do not call this method while iterating over either list.
4596 @GuardedBy("mNotificationLock")
4597 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4598 // Remove from both lists, either list could have a separate Record for what is
4599 // effectively the same notification.
4600 boolean wasPosted = false;
4601 NotificationRecord recordInList = null;
4602 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4604 mNotificationList.remove(recordInList);
4605 mNotificationsByKey.remove(recordInList.sbn.getKey());
4608 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4610 mEnqueuedNotifications.remove(recordInList);
4615 @GuardedBy("mNotificationLock")
4616 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4617 boolean wasPosted, String listenerName) {
4618 final String canceledKey = r.getKey();
4621 recordCallerLocked(r);
4625 if (r.getNotification().deleteIntent != null) {
4627 r.getNotification().deleteIntent.send();
4628 } catch (PendingIntent.CanceledException ex) {
4629 // do nothing - there's no relevant way to recover, and
4630 // no reason to let this propagate
4631 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4636 // Only cancel these if this notification actually got to be posted.
4639 if (r.getNotification().getSmallIcon() != null) {
4640 if (reason != REASON_SNOOZED) {
4641 r.isCanceled = true;
4643 mListeners.notifyRemovedLocked(r.sbn, reason);
4644 mHandler.post(new Runnable() {
4647 mGroupHelper.onNotificationRemoved(r.sbn);
4653 if (canceledKey.equals(mSoundNotificationKey)) {
4654 mSoundNotificationKey = null;
4655 final long identity = Binder.clearCallingIdentity();
4657 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4658 if (player != null) {
4661 } catch (RemoteException e) {
4663 Binder.restoreCallingIdentity(identity);
4668 if (canceledKey.equals(mVibrateNotificationKey)) {
4669 mVibrateNotificationKey = null;
4670 long identity = Binder.clearCallingIdentity();
4675 Binder.restoreCallingIdentity(identity);
4680 mLights.remove(canceledKey);
4683 // Record usage stats
4684 // TODO: add unbundling stats?
4687 case REASON_CANCEL_ALL:
4688 case REASON_LISTENER_CANCEL:
4689 case REASON_LISTENER_CANCEL_ALL:
4690 mUsageStats.registerDismissedByUser(r);
4692 case REASON_APP_CANCEL:
4693 case REASON_APP_CANCEL_ALL:
4694 mUsageStats.registerRemovedByApp(r);
4698 String groupKey = r.getGroupKey();
4699 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4700 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4701 mSummaryByGroupKey.remove(groupKey);
4703 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4704 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4705 summaries.remove(r.sbn.getPackageName());
4708 // Save it for users of getHistoricalNotifications()
4709 mArchive.record(r.sbn);
4711 final long now = System.currentTimeMillis();
4712 MetricsLogger.action(r.getLogMaker(now)
4713 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4714 .setType(MetricsEvent.TYPE_DISMISS)
4715 .setSubtype(reason));
4716 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4717 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
4721 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4722 * and none of the {@code mustNotHaveFlags}.
4724 void cancelNotification(final int callingUid, final int callingPid,
4725 final String pkg, final String tag, final int id,
4726 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4727 final int userId, final int reason, final ManagedServiceInfo listener) {
4728 // In enqueueNotificationInternal notifications are added by scheduling the
4729 // work on the worker handler. Hence, we also schedule the cancel on this
4730 // handler to avoid a scenario where an add notification call followed by a
4731 // remove notification call ends up in not removing the notification.
4732 mHandler.post(new Runnable() {
4735 String listenerName = listener == null ? null : listener.component.toShortString();
4736 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4737 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4739 synchronized (mNotificationLock) {
4740 // Look for the notification, searching both the posted and enqueued lists.
4741 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4743 // The notification was found, check if it should be removed.
4745 // Ideally we'd do this in the caller of this method. However, that would
4746 // require the caller to also find the notification.
4747 if (reason == REASON_CLICK) {
4748 mUsageStats.registerClickedByUser(r);
4751 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4754 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4758 // Cancel the notification.
4759 boolean wasPosted = removeFromNotificationListsLocked(r);
4760 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4761 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4763 updateLightsLocked();
4765 // No notification was found, assume that it is snoozed and cancel it.
4766 if (reason != REASON_SNOOZED) {
4767 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4779 * Determine whether the userId applies to the notification in question, either because
4780 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4782 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4784 // looking for USER_ALL notifications? match everything
4785 userId == UserHandle.USER_ALL
4786 // a notification sent to USER_ALL matches any query
4787 || r.getUserId() == UserHandle.USER_ALL
4788 // an exact user match
4789 || r.getUserId() == userId;
4793 * Determine whether the userId applies to the notification in question, either because
4794 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4795 * because it matches one of the users profiles.
4797 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4798 return notificationMatchesUserId(r, userId)
4799 || mUserProfiles.isCurrentProfile(r.getUserId());
4803 * Cancels all notifications from a given package that have all of the
4804 * {@code mustHaveFlags}.
4806 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4807 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4808 ManagedServiceInfo listener) {
4809 mHandler.post(new Runnable() {
4812 String listenerName = listener == null ? null : listener.component.toShortString();
4813 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4814 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4817 // Why does this parameter exist? Do we actually want to execute the above if doit
4823 synchronized (mNotificationLock) {
4824 FlagChecker flagChecker = (int flags) -> {
4825 if ((flags & mustHaveFlags) != mustHaveFlags) {
4828 if ((flags & mustNotHaveFlags) != 0) {
4833 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4834 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4835 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4836 listenerName, true /* wasPosted */);
4837 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4838 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4839 flagChecker, false /*includeCurrentProfiles*/, userId,
4840 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4841 mSnoozeHelper.cancel(userId, pkg);
4847 private interface FlagChecker {
4848 // Returns false if these flags do not pass the defined flag test.
4849 public boolean apply(int flags);
4852 @GuardedBy("mNotificationLock")
4853 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4854 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4855 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4856 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4857 ArrayList<NotificationRecord> canceledNotifications = null;
4858 for (int i = notificationList.size() - 1; i >= 0; --i) {
4859 NotificationRecord r = notificationList.get(i);
4860 if (includeCurrentProfiles) {
4861 if (!notificationMatchesCurrentProfiles(r, userId)) {
4864 } else if (!notificationMatchesUserId(r, userId)) {
4867 // Don't remove notifications to all, if there's no package name specified
4868 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4871 if (!flagChecker.apply(r.getFlags())) {
4874 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4877 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4880 if (canceledNotifications == null) {
4881 canceledNotifications = new ArrayList<>();
4883 notificationList.remove(i);
4884 mNotificationsByKey.remove(r.getKey());
4885 canceledNotifications.add(r);
4886 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4888 if (canceledNotifications != null) {
4889 final int M = canceledNotifications.size();
4890 for (int i = 0; i < M; i++) {
4891 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4892 listenerName, false /* sendDelete */, flagChecker);
4894 updateLightsLocked();
4898 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4899 ManagedServiceInfo listener) {
4900 String listenerName = listener == null ? null : listener.component.toShortString();
4901 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4906 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4907 snoozeCriterionId, listenerName));
4909 // Needs to post so that it can cancel notifications not yet enqueued.
4910 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4913 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4914 String listenerName = listener == null ? null : listener.component.toShortString();
4916 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4918 mSnoozeHelper.repost(key);
4922 @GuardedBy("mNotificationLock")
4923 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4924 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4925 mHandler.post(new Runnable() {
4928 synchronized (mNotificationLock) {
4929 String listenerName =
4930 listener == null ? null : listener.component.toShortString();
4931 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4932 null, userId, 0, 0, reason, listenerName);
4934 FlagChecker flagChecker = (int flags) -> {
4935 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4942 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4943 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4944 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4945 listenerName, true);
4946 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4947 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4948 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4949 reason, listenerName, false);
4950 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4956 // Warning: The caller is responsible for invoking updateLightsLocked().
4957 @GuardedBy("mNotificationLock")
4958 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4959 String listenerName, boolean sendDelete, FlagChecker flagChecker) {
4960 Notification n = r.getNotification();
4961 if (!n.isGroupSummary()) {
4965 String pkg = r.sbn.getPackageName();
4968 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4972 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4973 sendDelete, true, flagChecker);
4974 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4975 listenerName, sendDelete, false, flagChecker);
4978 @GuardedBy("mNotificationLock")
4979 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4980 NotificationRecord parentNotification, int callingUid, int callingPid,
4981 String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
4982 final String pkg = parentNotification.sbn.getPackageName();
4983 final int userId = parentNotification.getUserId();
4984 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4985 for (int i = notificationList.size() - 1; i >= 0; i--) {
4986 final NotificationRecord childR = notificationList.get(i);
4987 final StatusBarNotification childSbn = childR.sbn;
4988 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4989 childR.getGroupKey().equals(parentNotification.getGroupKey())
4990 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
4991 && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
4992 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4993 childSbn.getTag(), userId, 0, 0, reason, listenerName);
4994 notificationList.remove(i);
4995 mNotificationsByKey.remove(childR.getKey());
4996 cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName);
5001 @GuardedBy("mNotificationLock")
5002 void updateLightsLocked()
5004 // handle notification lights
5005 NotificationRecord ledNotification = null;
5006 while (ledNotification == null && !mLights.isEmpty()) {
5007 final String owner = mLights.get(mLights.size() - 1);
5008 ledNotification = mNotificationsByKey.get(owner);
5009 if (ledNotification == null) {
5010 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
5011 mLights.remove(owner);
5015 // Don't flash while we are in a call or screen is on
5016 if (ledNotification == null || mInCall || mScreenOn) {
5017 mNotificationLight.turnOff();
5019 NotificationRecord.Light light = ledNotification.getLight();
5020 if (light != null && mNotificationPulseEnabled) {
5022 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
5023 light.onMs, light.offMs);
5028 @GuardedBy("mNotificationLock")
5029 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
5030 String groupKey, int userId) {
5031 List<NotificationRecord> records = new ArrayList<>();
5032 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
5034 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
5039 @GuardedBy("mNotificationLock")
5040 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
5041 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
5042 List<NotificationRecord> records = new ArrayList<>();
5043 final int len = list.size();
5044 for (int i = 0; i < len; i++) {
5045 NotificationRecord r = list.get(i);
5046 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
5047 && r.sbn.getPackageName().equals(pkg)) {
5054 // Searches both enqueued and posted notifications by key.
5055 // TODO: need to combine a bunch of these getters with slightly different behavior.
5056 // TODO: Should enqueuing just add to mNotificationsByKey instead?
5057 @GuardedBy("mNotificationLock")
5058 private NotificationRecord findNotificationByKeyLocked(String key) {
5059 NotificationRecord r;
5060 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
5063 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
5069 @GuardedBy("mNotificationLock")
5070 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
5071 NotificationRecord r;
5072 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
5075 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
5082 @GuardedBy("mNotificationLock")
5083 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5084 String pkg, String tag, int id, int userId) {
5085 final int len = list.size();
5086 for (int i = 0; i < len; i++) {
5087 NotificationRecord r = list.get(i);
5088 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
5089 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
5096 @GuardedBy("mNotificationLock")
5097 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5099 final int N = list.size();
5100 for (int i = 0; i < N; i++) {
5101 if (key.equals(list.get(i).getKey())) {
5108 @GuardedBy("mNotificationLock")
5109 int indexOfNotificationLocked(String key) {
5110 final int N = mNotificationList.size();
5111 for (int i = 0; i < N; i++) {
5112 if (key.equals(mNotificationList.get(i).getKey())) {
5119 private void updateNotificationPulse() {
5120 synchronized (mNotificationLock) {
5121 updateLightsLocked();
5125 protected boolean isCallingUidSystem() {
5126 final int uid = Binder.getCallingUid();
5127 return uid == Process.SYSTEM_UID;
5130 protected boolean isUidSystemOrPhone(int uid) {
5131 final int appid = UserHandle.getAppId(uid);
5132 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
5135 // TODO: Most calls should probably move to isCallerSystem.
5136 protected boolean isCallerSystemOrPhone() {
5137 return isUidSystemOrPhone(Binder.getCallingUid());
5140 private void checkCallerIsSystemOrShell() {
5141 if (Binder.getCallingUid() == Process.SHELL_UID) {
5144 checkCallerIsSystem();
5147 private void checkCallerIsSystem() {
5148 if (isCallerSystemOrPhone()) {
5151 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
5154 private void checkCallerIsSystemOrSameApp(String pkg) {
5155 if (isCallerSystemOrPhone()) {
5158 checkCallerIsSameApp(pkg);
5161 private boolean isCallerInstantApp(String pkg) {
5162 // System is always allowed to act for ephemeral apps.
5163 if (isCallerSystemOrPhone()) {
5167 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
5170 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
5171 UserHandle.getCallingUserId());
5173 throw new SecurityException("Unknown package " + pkg);
5175 return ai.isInstantApp();
5176 } catch (RemoteException re) {
5177 throw new SecurityException("Unknown package " + pkg, re);
5182 private void checkCallerIsSameApp(String pkg) {
5183 final int uid = Binder.getCallingUid();
5185 ApplicationInfo ai = mPackageManager.getApplicationInfo(
5186 pkg, 0, UserHandle.getCallingUserId());
5188 throw new SecurityException("Unknown package " + pkg);
5190 if (!UserHandle.isSameApp(ai.uid, uid)) {
5191 throw new SecurityException("Calling uid " + uid + " gave package "
5192 + pkg + " which is owned by uid " + ai.uid);
5194 } catch (RemoteException re) {
5195 throw new SecurityException("Unknown package " + pkg + "\n" + re);
5199 private static String callStateToString(int state) {
5201 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
5202 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
5203 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
5204 default: return "CALL_STATE_UNKNOWN_" + state;
5208 private void listenForCallState() {
5209 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
5211 public void onCallStateChanged(int state, String incomingNumber) {
5212 if (mCallState == state) return;
5213 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
5216 }, PhoneStateListener.LISTEN_CALL_STATE);
5220 * Generates a NotificationRankingUpdate from 'sbns', considering only
5221 * notifications visible to the given listener.
5223 @GuardedBy("mNotificationLock")
5224 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
5225 final int N = mNotificationList.size();
5226 ArrayList<String> keys = new ArrayList<String>(N);
5227 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
5228 ArrayList<Integer> importance = new ArrayList<>(N);
5229 Bundle overrideGroupKeys = new Bundle();
5230 Bundle visibilityOverrides = new Bundle();
5231 Bundle suppressedVisualEffects = new Bundle();
5232 Bundle explanation = new Bundle();
5233 Bundle channels = new Bundle();
5234 Bundle overridePeople = new Bundle();
5235 Bundle snoozeCriteria = new Bundle();
5236 Bundle showBadge = new Bundle();
5237 for (int i = 0; i < N; i++) {
5238 NotificationRecord record = mNotificationList.get(i);
5239 if (!isVisibleToListener(record.sbn, info)) {
5242 final String key = record.sbn.getKey();
5244 importance.add(record.getImportance());
5245 if (record.getImportanceExplanation() != null) {
5246 explanation.putCharSequence(key, record.getImportanceExplanation());
5248 if (record.isIntercepted()) {
5249 interceptedKeys.add(key);
5252 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
5253 if (record.getPackageVisibilityOverride()
5254 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
5255 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
5257 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
5258 channels.putParcelable(key, record.getChannel());
5259 overridePeople.putStringArrayList(key, record.getPeopleOverride());
5260 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
5261 showBadge.putBoolean(key, record.canShowBadge());
5263 final int M = keys.size();
5264 String[] keysAr = keys.toArray(new String[M]);
5265 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
5266 int[] importanceAr = new int[M];
5267 for (int i = 0; i < M; i++) {
5268 importanceAr[i] = importance.get(i);
5270 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
5271 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
5272 channels, overridePeople, snoozeCriteria, showBadge);
5275 boolean hasCompanionDevice(ManagedServiceInfo info) {
5276 if (mCompanionManager == null) {
5277 mCompanionManager = getCompanionManager();
5279 // Companion mgr doesn't exist on all device types
5280 if (mCompanionManager == null) {
5283 long identity = Binder.clearCallingIdentity();
5285 List<String> associations = mCompanionManager.getAssociations(
5286 info.component.getPackageName(), info.userid);
5287 if (!ArrayUtils.isEmpty(associations)) {
5290 } catch (SecurityException se) {
5291 // Not a privileged listener
5292 } catch (RemoteException re) {
5293 Slog.e(TAG, "Cannot reach companion device service", re);
5294 } catch (Exception e) {
5295 Slog.e(TAG, "Cannot verify listener " + info, e);
5297 Binder.restoreCallingIdentity(identity);
5302 protected ICompanionDeviceManager getCompanionManager() {
5303 return ICompanionDeviceManager.Stub.asInterface(
5304 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
5307 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5308 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5311 // TODO: remove this for older listeners.
5315 private boolean isPackageSuspendedForUser(String pkg, int uid) {
5316 int userId = UserHandle.getUserId(uid);
5318 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
5319 } catch (RemoteException re) {
5320 throw new SecurityException("Could not talk to package manager service");
5321 } catch (IllegalArgumentException ex) {
5322 // Package not found.
5327 private class TrimCache {
5328 StatusBarNotification heavy;
5329 StatusBarNotification sbnClone;
5330 StatusBarNotification sbnCloneLight;
5332 TrimCache(StatusBarNotification sbn) {
5336 StatusBarNotification ForListener(ManagedServiceInfo info) {
5337 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5338 if (sbnCloneLight == null) {
5339 sbnCloneLight = heavy.cloneLight();
5341 return sbnCloneLight;
5343 if (sbnClone == null) {
5344 sbnClone = heavy.clone();
5351 public class NotificationAssistants extends ManagedServices {
5352 static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
5354 public NotificationAssistants(IPackageManager pm) {
5355 super(getContext(), mNotificationLock, mUserProfiles, pm);
5359 protected Config getConfig() {
5360 Config c = new Config();
5361 c.caption = "notification assistant service";
5362 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5363 c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
5364 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5365 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5366 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5367 c.clientLabel = R.string.notification_ranker_binding_label;
5372 protected IInterface asInterface(IBinder binder) {
5373 return INotificationListener.Stub.asInterface(binder);
5377 protected boolean checkType(IInterface service) {
5378 return service instanceof INotificationListener;
5382 protected void onServiceAdded(ManagedServiceInfo info) {
5383 mListeners.registerGuestService(info);
5387 @GuardedBy("mNotificationLock")
5388 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5389 mListeners.unregisterService(removed.service, removed.userid);
5392 public void onNotificationEnqueued(final NotificationRecord r) {
5393 final StatusBarNotification sbn = r.sbn;
5394 TrimCache trimCache = new TrimCache(sbn);
5396 // There should be only one, but it's a list, so while we enforce
5397 // singularity elsewhere, we keep it general here, to avoid surprises.
5398 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5399 boolean sbnVisible = isVisibleToListener(sbn, info);
5404 final int importance = r.getImportance();
5405 final boolean fromUser = r.isImportanceFromUser();
5406 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5407 mHandler.post(new Runnable() {
5410 notifyEnqueued(info, sbnToPost);
5416 private void notifyEnqueued(final ManagedServiceInfo info,
5417 final StatusBarNotification sbn) {
5418 final INotificationListener assistant = (INotificationListener) info.service;
5419 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5421 assistant.onNotificationEnqueued(sbnHolder);
5422 } catch (RemoteException ex) {
5423 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5428 * asynchronously notify the assistant that a notification has been snoozed until a
5431 @GuardedBy("mNotificationLock")
5432 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5433 final String snoozeCriterionId) {
5434 TrimCache trimCache = new TrimCache(sbn);
5435 for (final ManagedServiceInfo info : getServices()) {
5436 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5437 mHandler.post(new Runnable() {
5440 final INotificationListener assistant =
5441 (INotificationListener) info.service;
5442 StatusBarNotificationHolder sbnHolder
5443 = new StatusBarNotificationHolder(sbnToPost);
5445 assistant.onNotificationSnoozedUntilContext(
5446 sbnHolder, snoozeCriterionId);
5447 } catch (RemoteException ex) {
5448 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5455 public boolean isEnabled() {
5456 return !getServices().isEmpty();
5460 public class NotificationListeners extends ManagedServices {
5461 static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
5463 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5465 public NotificationListeners(IPackageManager pm) {
5466 super(getContext(), mNotificationLock, mUserProfiles, pm);
5471 protected Config getConfig() {
5472 Config c = new Config();
5473 c.caption = "notification listener";
5474 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5475 c.xmlTag = TAG_ENABLED_NOTIFICATION_LISTENERS;
5476 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5477 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5478 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5479 c.clientLabel = R.string.notification_listener_binding_label;
5484 protected IInterface asInterface(IBinder binder) {
5485 return INotificationListener.Stub.asInterface(binder);
5489 protected boolean checkType(IInterface service) {
5490 return service instanceof INotificationListener;
5494 public void onServiceAdded(ManagedServiceInfo info) {
5495 final INotificationListener listener = (INotificationListener) info.service;
5496 final NotificationRankingUpdate update;
5497 synchronized (mNotificationLock) {
5498 update = makeRankingUpdateLocked(info);
5501 listener.onListenerConnected(update);
5502 } catch (RemoteException e) {
5508 @GuardedBy("mNotificationLock")
5509 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5510 if (removeDisabledHints(removed)) {
5511 updateListenerHintsLocked();
5512 updateEffectsSuppressorLocked();
5514 mLightTrimListeners.remove(removed);
5517 @GuardedBy("mNotificationLock")
5518 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5519 if (trim == TRIM_LIGHT) {
5520 mLightTrimListeners.add(info);
5522 mLightTrimListeners.remove(info);
5526 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5527 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5531 * asynchronously notify all listeners about a new notification
5534 * Also takes care of removing a notification that has been visible to a listener before,
5535 * but isn't anymore.
5537 @GuardedBy("mNotificationLock")
5538 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5539 // Lazily initialized snapshots of the notification.
5540 TrimCache trimCache = new TrimCache(sbn);
5542 for (final ManagedServiceInfo info : getServices()) {
5543 boolean sbnVisible = isVisibleToListener(sbn, info);
5544 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5545 // This notification hasn't been and still isn't visible -> ignore.
5546 if (!oldSbnVisible && !sbnVisible) {
5549 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5551 // This notification became invisible -> remove the old one.
5552 if (oldSbnVisible && !sbnVisible) {
5553 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5554 mHandler.post(new Runnable() {
5557 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5563 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5564 mHandler.post(new Runnable() {
5567 notifyPosted(info, sbnToPost, update);
5574 * asynchronously notify all listeners about a removed notification
5576 @GuardedBy("mNotificationLock")
5577 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5578 // make a copy in case changes are made to the underlying Notification object
5579 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5581 final StatusBarNotification sbnLight = sbn.cloneLight();
5582 for (final ManagedServiceInfo info : getServices()) {
5583 if (!isVisibleToListener(sbn, info)) {
5586 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5587 mHandler.post(new Runnable() {
5590 notifyRemoved(info, sbnLight, update, reason);
5597 * asynchronously notify all listeners about a reordering of notifications
5599 @GuardedBy("mNotificationLock")
5600 public void notifyRankingUpdateLocked() {
5601 for (final ManagedServiceInfo serviceInfo : getServices()) {
5602 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5605 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5606 mHandler.post(new Runnable() {
5609 notifyRankingUpdate(serviceInfo, update);
5615 @GuardedBy("mNotificationLock")
5616 public void notifyListenerHintsChangedLocked(final int hints) {
5617 for (final ManagedServiceInfo serviceInfo : getServices()) {
5618 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5621 mHandler.post(new Runnable() {
5624 notifyListenerHintsChanged(serviceInfo, hints);
5630 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5631 for (final ManagedServiceInfo serviceInfo : getServices()) {
5632 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5635 mHandler.post(new Runnable() {
5638 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5644 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5645 final NotificationChannel channel, final int modificationType) {
5646 if (channel == null) {
5649 for (final ManagedServiceInfo serviceInfo : getServices()) {
5650 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5654 BackgroundThread.getHandler().post(() -> {
5655 if (hasCompanionDevice(serviceInfo)) {
5656 notifyNotificationChannelChanged(
5657 serviceInfo, pkg, user, channel, modificationType);
5663 protected void notifyNotificationChannelGroupChanged(
5664 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5665 final int modificationType) {
5666 if (group == null) {
5669 for (final ManagedServiceInfo serviceInfo : getServices()) {
5670 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5674 BackgroundThread.getHandler().post(() -> {
5675 if (hasCompanionDevice(serviceInfo)) {
5676 notifyNotificationChannelGroupChanged(
5677 serviceInfo, pkg, user, group, modificationType);
5683 private void notifyPosted(final ManagedServiceInfo info,
5684 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5685 final INotificationListener listener = (INotificationListener) info.service;
5686 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5688 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5689 } catch (RemoteException ex) {
5690 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5694 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5695 NotificationRankingUpdate rankingUpdate, int reason) {
5696 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5699 final INotificationListener listener = (INotificationListener) info.service;
5700 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5702 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5703 } catch (RemoteException ex) {
5704 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5708 private void notifyRankingUpdate(ManagedServiceInfo info,
5709 NotificationRankingUpdate rankingUpdate) {
5710 final INotificationListener listener = (INotificationListener) info.service;
5712 listener.onNotificationRankingUpdate(rankingUpdate);
5713 } catch (RemoteException ex) {
5714 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5718 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5719 final INotificationListener listener = (INotificationListener) info.service;
5721 listener.onListenerHintsChanged(hints);
5722 } catch (RemoteException ex) {
5723 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5727 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5728 int interruptionFilter) {
5729 final INotificationListener listener = (INotificationListener) info.service;
5731 listener.onInterruptionFilterChanged(interruptionFilter);
5732 } catch (RemoteException ex) {
5733 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5737 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5738 final String pkg, final UserHandle user, final NotificationChannel channel,
5739 final int modificationType) {
5740 final INotificationListener listener = (INotificationListener) info.service;
5742 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5743 } catch (RemoteException ex) {
5744 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5748 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5749 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5750 final int modificationType) {
5751 final INotificationListener listener = (INotificationListener) info.service;
5753 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5754 } catch (RemoteException ex) {
5755 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5759 public boolean isListenerPackage(String packageName) {
5760 if (packageName == null) {
5763 // TODO: clean up locking object later
5764 synchronized (mNotificationLock) {
5765 for (final ManagedServiceInfo serviceInfo : getServices()) {
5766 if (packageName.equals(serviceInfo.component.getPackageName())) {
5775 public static final class DumpFilter {
5776 public boolean filtered = false;
5777 public String pkgFilter;
5780 public boolean stats;
5781 public boolean redact = true;
5782 public boolean proto = false;
5784 public static DumpFilter parseFromArguments(String[] args) {
5785 final DumpFilter filter = new DumpFilter();
5786 for (int ai = 0; ai < args.length; ai++) {
5787 final String a = args[ai];
5788 if ("--proto".equals(args[0])) {
5789 filter.proto = true;
5791 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5792 filter.redact = false;
5793 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5794 if (ai < args.length-1) {
5796 filter.pkgFilter = args[ai].trim().toLowerCase();
5797 if (filter.pkgFilter.isEmpty()) {
5798 filter.pkgFilter = null;
5800 filter.filtered = true;
5803 } else if ("--zen".equals(a) || "zen".equals(a)) {
5804 filter.filtered = true;
5806 } else if ("--stats".equals(a)) {
5807 filter.stats = true;
5808 if (ai < args.length-1) {
5810 filter.since = Long.parseLong(args[ai]);
5819 public boolean matches(StatusBarNotification sbn) {
5820 if (!filtered) return true;
5821 return zen ? true : sbn != null
5822 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5825 public boolean matches(ComponentName component) {
5826 if (!filtered) return true;
5827 return zen ? true : component != null && matches(component.getPackageName());
5830 public boolean matches(String pkg) {
5831 if (!filtered) return true;
5832 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5836 public String toString() {
5837 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5842 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5843 * binder without sending large amounts of data over a oneway transaction.
5845 private static final class StatusBarNotificationHolder
5846 extends IStatusBarNotificationHolder.Stub {
5847 private StatusBarNotification mValue;
5849 public StatusBarNotificationHolder(StatusBarNotification value) {
5853 /** Get the held value and clear it. This function should only be called once per holder */
5855 public StatusBarNotification get() {
5856 StatusBarNotification value = mValue;
5862 private class ShellCmd extends ShellCommand {
5863 public static final String USAGE = "help\n"
5864 + "allow_listener COMPONENT [user_id]\n"
5865 + "disallow_listener COMPONENT [user_id]\n"
5866 + "set_assistant COMPONENT\n"
5867 + "remove_assistant COMPONENT\n"
5868 + "allow_dnd PACKAGE\n"
5869 + "disallow_dnd PACKAGE";
5872 public int onCommand(String cmd) {
5874 return handleDefaultCommands(cmd);
5876 final PrintWriter pw = getOutPrintWriter();
5880 getBinderService().setNotificationPolicyAccessGranted(
5881 getNextArgRequired(), true);
5885 case "disallow_dnd": {
5886 getBinderService().setNotificationPolicyAccessGranted(
5887 getNextArgRequired(), false);
5890 case "allow_listener": {
5891 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5893 pw.println("Invalid listener - must be a ComponentName");
5896 String userId = getNextArg();
5897 if (userId == null) {
5898 getBinderService().setNotificationListenerAccessGranted(cn, true);
5900 getBinderService().setNotificationListenerAccessGrantedForUser(
5901 cn, Integer.parseInt(userId), true);
5905 case "disallow_listener": {
5906 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5908 pw.println("Invalid listener - must be a ComponentName");
5911 String userId = getNextArg();
5912 if (userId == null) {
5913 getBinderService().setNotificationListenerAccessGranted(cn, false);
5915 getBinderService().setNotificationListenerAccessGrantedForUser(
5916 cn, Integer.parseInt(userId), false);
5920 case "allow_assistant": {
5921 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5923 pw.println("Invalid assistant - must be a ComponentName");
5926 getBinderService().setNotificationAssistantAccessGranted(cn, true);
5929 case "disallow_assistant": {
5930 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5932 pw.println("Invalid assistant - must be a ComponentName");
5935 getBinderService().setNotificationAssistantAccessGranted(cn, false);
5940 return handleDefaultCommands(cmd);
5942 } catch (Exception e) {
5943 pw.println("Error occurred. Check logcat for details. " + e.getMessage());
5944 Slog.e(TAG, "Error running shell command", e);
5950 public void onHelp() {
5951 getOutPrintWriter().println(USAGE);