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;
1199 // TODO: All tests should use this init instead of the one-off setters above.
1201 void init(Looper looper, IPackageManager packageManager,
1202 PackageManager packageManagerClient,
1203 LightsManager lightsManager, NotificationListeners notificationListeners,
1204 NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
1205 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1206 NotificationUsageStats usageStats, AtomicFile policyFile,
1207 ActivityManager activityManager, GroupHelper groupHelper) {
1208 Resources resources = getContext().getResources();
1209 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1210 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1211 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1213 mAccessibilityManager =
1214 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1215 mAm = ActivityManager.getService();
1216 mPackageManager = packageManager;
1217 mPackageManagerClient = packageManagerClient;
1218 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1219 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1220 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1221 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1222 mCompanionManager = companionManager;
1223 mActivityManager = activityManager;
1225 mHandler = new WorkerHandler(looper);
1226 mRankingThread.start();
1227 String[] extractorNames;
1229 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1230 } catch (Resources.NotFoundException e) {
1231 extractorNames = new String[0];
1233 mUsageStats = usageStats;
1234 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1235 mRankingHelper = new RankingHelper(getContext(),
1236 mPackageManagerClient,
1240 mConditionProviders = conditionProviders;
1241 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1242 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1244 public void onConfigChanged() {
1249 void onZenModeChanged() {
1250 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1251 getContext().sendBroadcastAsUser(
1252 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1253 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1254 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1255 synchronized (mNotificationLock) {
1256 updateInterruptionFilterLocked();
1261 void onPolicyChanged() {
1262 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1265 mSnoozeHelper = snoozeHelper;
1266 mGroupHelper = groupHelper;
1268 // This is a ManagedServices object that keeps track of the listeners.
1269 mListeners = notificationListeners;
1271 // This is a MangedServices object that keeps track of the assistant.
1272 mAssistants = notificationAssistants;
1274 mPolicyFile = policyFile;
1277 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1278 if (mStatusBar != null) {
1279 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1282 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1283 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1285 mFallbackVibrationPattern = getLongArray(resources,
1286 R.array.config_notificationFallbackVibePattern,
1287 VIBRATE_PATTERN_MAXLEN,
1288 DEFAULT_VIBRATE_PATTERN);
1289 mInCallNotificationUri = Uri.parse("file://" +
1290 resources.getString(R.string.config_inCallNotificationSound));
1291 mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
1292 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
1293 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
1295 mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
1297 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1299 // Don't start allowing notifications until the setup wizard has run once.
1300 // After that, including subsequent boots, init with notifications turned on.
1301 // This works on the first boot because the setup wizard will toggle this
1302 // flag at least once and we'll go back to 0 after that.
1303 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1304 Settings.Global.DEVICE_PROVISIONED, 0)) {
1305 mDisableNotificationEffects = true;
1307 mZenModeHelper.initZenMode();
1308 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1310 mUserProfiles.updateCache(getContext());
1311 listenForCallState();
1313 mSettingsObserver = new SettingsObserver(mHandler);
1315 mArchive = new Archive(resources.getInteger(
1316 R.integer.config_notificationServiceArchiveSize));
1318 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1319 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1323 public void onStart() {
1324 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1326 public void repost(int userId, NotificationRecord r) {
1329 Slog.d(TAG, "Reposting " + r.getKey());
1331 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1332 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1333 r.sbn.getNotification(), userId);
1334 } catch (Exception e) {
1335 Slog.e(TAG, "Cannot un-snooze notification", e);
1340 final File systemDir = new File(Environment.getDataDirectory(), "system");
1342 init(Looper.myLooper(),
1343 AppGlobals.getPackageManager(), getContext().getPackageManager(),
1344 getLocalService(LightsManager.class),
1345 new NotificationListeners(AppGlobals.getPackageManager()),
1346 new NotificationAssistants(AppGlobals.getPackageManager()),
1347 new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
1348 null, snoozeHelper, new NotificationUsageStats(getContext()),
1349 new AtomicFile(new File(systemDir, "notification_policy.xml")),
1350 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
1353 // register for various Intents
1354 IntentFilter filter = new IntentFilter();
1355 filter.addAction(Intent.ACTION_SCREEN_ON);
1356 filter.addAction(Intent.ACTION_SCREEN_OFF);
1357 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1358 filter.addAction(Intent.ACTION_USER_PRESENT);
1359 filter.addAction(Intent.ACTION_USER_STOPPED);
1360 filter.addAction(Intent.ACTION_USER_SWITCHED);
1361 filter.addAction(Intent.ACTION_USER_ADDED);
1362 filter.addAction(Intent.ACTION_USER_REMOVED);
1363 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1364 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1365 getContext().registerReceiver(mIntentReceiver, filter);
1367 IntentFilter pkgFilter = new IntentFilter();
1368 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1369 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1370 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1371 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1372 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1373 pkgFilter.addDataScheme("package");
1374 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1377 IntentFilter suspendedPkgFilter = new IntentFilter();
1378 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1379 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1380 suspendedPkgFilter, null, null);
1382 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1383 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1386 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1387 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1388 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1390 IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
1391 getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
1393 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
1394 getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
1396 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1397 publishLocalService(NotificationManagerInternal.class, mInternalService);
1400 private GroupHelper getGroupHelper() {
1401 return new GroupHelper(new GroupHelper.Callback() {
1403 public void addAutoGroup(String key) {
1404 synchronized (mNotificationLock) {
1405 addAutogroupKeyLocked(key);
1410 public void removeAutoGroup(String key) {
1411 synchronized (mNotificationLock) {
1412 removeAutogroupKeyLocked(key);
1417 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1418 createAutoGroupSummary(userId, pkg, triggeringKey);
1422 public void removeAutoGroupSummary(int userId, String pkg) {
1423 synchronized (mNotificationLock) {
1424 clearAutogroupSummaryLocked(userId, pkg);
1430 private void sendRegisteredOnlyBroadcast(String action) {
1431 getContext().sendBroadcastAsUser(new Intent(action)
1432 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1436 public void onBootPhase(int phase) {
1437 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1438 // no beeping until we're basically done booting
1439 mSystemReady = true;
1441 // Grab our optional AudioService
1442 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1443 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1444 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1445 mZenModeHelper.onSystemReady();
1446 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1447 // This observer will force an update when observe is called, causing us to
1448 // bind to listener services.
1449 mSettingsObserver.observe();
1450 mListeners.onBootPhaseAppsCanStart();
1451 mAssistants.onBootPhaseAppsCanStart();
1452 mConditionProviders.onBootPhaseAppsCanStart();
1456 @GuardedBy("mNotificationLock")
1457 private void updateListenerHintsLocked() {
1458 final int hints = calculateHints();
1459 if (hints == mListenerHints) return;
1460 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1461 mListenerHints = hints;
1462 scheduleListenerHintsChanged(hints);
1465 @GuardedBy("mNotificationLock")
1466 private void updateEffectsSuppressorLocked() {
1467 final long updatedSuppressedEffects = calculateSuppressedEffects();
1468 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1469 final List<ComponentName> suppressors = getSuppressors();
1470 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1471 mEffectsSuppressors = suppressors;
1472 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1473 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1476 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1477 boolean fromListener) {
1478 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1480 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1481 UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
1483 if (isUidSystemOrPhone(uid)) {
1484 int[] profileIds = mUserProfiles.getCurrentProfileIds();
1485 int N = profileIds.length;
1486 for (int i = 0; i < N; i++) {
1487 int profileId = profileIds[i];
1488 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1489 profileId, REASON_CHANNEL_BANNED,
1494 mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
1496 if (!fromListener) {
1497 final NotificationChannel modifiedChannel =
1498 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1499 mListeners.notifyNotificationChannelChanged(
1500 pkg, UserHandle.getUserHandleForUid(uid),
1501 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1507 private ArrayList<ComponentName> getSuppressors() {
1508 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1509 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1510 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1512 for (ManagedServiceInfo info : serviceInfoList) {
1513 names.add(info.component);
1520 private boolean removeDisabledHints(ManagedServiceInfo info) {
1521 return removeDisabledHints(info, 0);
1524 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1525 boolean removed = false;
1527 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1528 final int hint = mListenersDisablingEffects.keyAt(i);
1529 final ArraySet<ManagedServiceInfo> listeners =
1530 mListenersDisablingEffects.valueAt(i);
1532 if (hints == 0 || (hint & hints) == hint) {
1533 removed = removed || listeners.remove(info);
1540 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1541 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1542 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1545 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1546 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1549 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1550 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1554 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1555 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1556 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1559 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1560 hintListeners.add(info);
1563 private int calculateHints() {
1565 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1566 int hint = mListenersDisablingEffects.keyAt(i);
1567 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1569 if (!serviceInfoList.isEmpty()) {
1577 private long calculateSuppressedEffects() {
1578 int hints = calculateHints();
1579 long suppressedEffects = 0;
1581 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1582 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1585 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1586 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1589 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1590 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1593 return suppressedEffects;
1596 @GuardedBy("mNotificationLock")
1597 private void updateInterruptionFilterLocked() {
1598 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1599 if (interruptionFilter == mInterruptionFilter) return;
1600 mInterruptionFilter = interruptionFilter;
1601 scheduleInterruptionFilterChanged(interruptionFilter);
1605 INotificationManager getBinderService() {
1606 return INotificationManager.Stub.asInterface(mService);
1610 NotificationManagerInternal getInternalService() {
1611 return mInternalService;
1614 private final IBinder mService = new INotificationManager.Stub() {
1616 // ============================================================================
1619 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1622 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1623 + " duration=" + duration);
1626 if (pkg == null || callback == null) {
1627 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1630 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1631 final boolean isPackageSuspended =
1632 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1634 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1635 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1636 || isPackageSuspended)) {
1637 Slog.e(TAG, "Suppressing toast from package " + pkg
1638 + (isPackageSuspended
1639 ? " due to package suspended by administrator."
1640 : " by user request."));
1644 synchronized (mToastQueue) {
1645 int callingPid = Binder.getCallingPid();
1646 long callingId = Binder.clearCallingIdentity();
1650 // All packages aside from the android package can enqueue one toast at a time
1651 if (!isSystemToast) {
1652 index = indexOfToastPackageLocked(pkg);
1654 index = indexOfToastLocked(pkg, callback);
1657 // If the package already has a toast, we update its toast
1658 // in the queue, we don't move it to the end of the queue.
1660 record = mToastQueue.get(index);
1661 record.update(duration);
1662 record.update(callback);
1664 Binder token = new Binder();
1665 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1666 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1667 mToastQueue.add(record);
1668 index = mToastQueue.size() - 1;
1670 keepProcessAliveIfNeededLocked(callingPid);
1671 // If it's at index 0, it's the current toast. It doesn't matter if it's
1672 // new or just been updated. Call back and tell it to show itself.
1673 // If the callback fails, this will remove it from the list, so don't
1674 // assume that it's valid after this.
1676 showNextToastLocked();
1679 Binder.restoreCallingIdentity(callingId);
1685 public void cancelToast(String pkg, ITransientNotification callback) {
1686 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1688 if (pkg == null || callback == null) {
1689 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1693 synchronized (mToastQueue) {
1694 long callingId = Binder.clearCallingIdentity();
1696 int index = indexOfToastLocked(pkg, callback);
1698 cancelToastLocked(index);
1700 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1701 + " callback=" + callback);
1704 Binder.restoreCallingIdentity(callingId);
1710 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1711 Notification notification, int userId) throws RemoteException {
1712 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1713 Binder.getCallingPid(), tag, id, notification, userId);
1717 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1718 checkCallerIsSystemOrSameApp(pkg);
1719 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1720 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1721 // Don't allow client applications to cancel foreground service notis or autobundled
1723 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1724 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1725 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1726 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1730 public void cancelAllNotifications(String pkg, int userId) {
1731 checkCallerIsSystemOrSameApp(pkg);
1733 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1734 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1736 // Calling from user space, don't allow the canceling of actively
1737 // running foreground services.
1738 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1739 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1740 REASON_APP_CANCEL_ALL, null);
1744 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1745 checkCallerIsSystem();
1747 mRankingHelper.setEnabled(pkg, uid, enabled);
1748 // Now, cancel any outstanding notifications that are part of a just-disabled app
1750 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1751 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1757 * Use this when you just want to know if notifications are OK for this package.
1760 public boolean areNotificationsEnabled(String pkg) {
1761 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1765 * Use this when you just want to know if notifications are OK for this package.
1768 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1769 checkCallerIsSystemOrSameApp(pkg);
1770 if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) {
1771 getContext().enforceCallingPermission(
1772 android.Manifest.permission.INTERACT_ACROSS_USERS,
1773 "canNotifyAsPackage for uid " + uid);
1776 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1780 public int getPackageImportance(String pkg) {
1781 checkCallerIsSystemOrSameApp(pkg);
1782 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1786 public boolean canShowBadge(String pkg, int uid) {
1787 checkCallerIsSystem();
1788 return mRankingHelper.canShowBadge(pkg, uid);
1792 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1793 checkCallerIsSystem();
1794 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1799 public void createNotificationChannelGroups(String pkg,
1800 ParceledListSlice channelGroupList) throws RemoteException {
1801 checkCallerIsSystemOrSameApp(pkg);
1802 List<NotificationChannelGroup> groups = channelGroupList.getList();
1803 final int groupSize = groups.size();
1804 for (int i = 0; i < groupSize; i++) {
1805 final NotificationChannelGroup group = groups.get(i);
1806 Preconditions.checkNotNull(group, "group in list is null");
1807 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1808 true /* fromTargetApp */);
1809 mListeners.notifyNotificationChannelGroupChanged(pkg,
1810 UserHandle.of(UserHandle.getCallingUserId()), group,
1811 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1816 private void createNotificationChannelsImpl(String pkg, int uid,
1817 ParceledListSlice channelsList) {
1818 List<NotificationChannel> channels = channelsList.getList();
1819 final int channelsSize = channels.size();
1820 for (int i = 0; i < channelsSize; i++) {
1821 final NotificationChannel channel = channels.get(i);
1822 Preconditions.checkNotNull(channel, "channel in list is null");
1823 mRankingHelper.createNotificationChannel(pkg, uid, channel,
1824 true /* fromTargetApp */);
1825 mListeners.notifyNotificationChannelChanged(pkg,
1826 UserHandle.getUserHandleForUid(uid),
1827 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1828 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1834 public void createNotificationChannels(String pkg,
1835 ParceledListSlice channelsList) throws RemoteException {
1836 checkCallerIsSystemOrSameApp(pkg);
1837 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1841 public void createNotificationChannelsForPackage(String pkg, int uid,
1842 ParceledListSlice channelsList) throws RemoteException {
1843 checkCallerIsSystem();
1844 createNotificationChannelsImpl(pkg, uid, channelsList);
1848 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1849 checkCallerIsSystemOrSameApp(pkg);
1850 return mRankingHelper.getNotificationChannel(
1851 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1855 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1856 String channelId, boolean includeDeleted) {
1857 checkCallerIsSystem();
1858 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1862 public void deleteNotificationChannel(String pkg, String channelId) {
1863 checkCallerIsSystemOrSameApp(pkg);
1864 final int callingUid = Binder.getCallingUid();
1865 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1866 throw new IllegalArgumentException("Cannot delete default channel");
1868 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1869 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1870 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1871 mListeners.notifyNotificationChannelChanged(pkg,
1872 UserHandle.getUserHandleForUid(callingUid),
1873 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1874 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1879 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1881 checkCallerIsSystemOrSameApp(pkg);
1882 return new ParceledListSlice<>(new ArrayList(
1883 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1887 public void deleteNotificationChannelGroup(String pkg, String groupId) {
1888 checkCallerIsSystemOrSameApp(pkg);
1890 final int callingUid = Binder.getCallingUid();
1891 NotificationChannelGroup groupToDelete =
1892 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1893 if (groupToDelete != null) {
1894 List<NotificationChannel> deletedChannels =
1895 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1896 for (int i = 0; i < deletedChannels.size(); i++) {
1897 final NotificationChannel deletedChannel = deletedChannels.get(i);
1898 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1900 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1902 mListeners.notifyNotificationChannelChanged(pkg,
1903 UserHandle.getUserHandleForUid(callingUid),
1905 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1907 mListeners.notifyNotificationChannelGroupChanged(
1908 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1909 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1915 public void updateNotificationChannelForPackage(String pkg, int uid,
1916 NotificationChannel channel) {
1917 enforceSystemOrSystemUI("Caller not system or systemui");
1918 Preconditions.checkNotNull(channel);
1919 updateNotificationChannelInt(pkg, uid, channel, false);
1923 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1924 int uid, boolean includeDeleted) {
1925 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1926 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1930 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1931 boolean includeDeleted) {
1932 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1933 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1938 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1939 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1940 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1944 public int getDeletedChannelCount(String pkg, int uid) {
1945 enforceSystemOrSystemUI("getDeletedChannelCount");
1946 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1950 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1951 String pkg, int uid, boolean includeDeleted) {
1952 checkCallerIsSystem();
1953 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1957 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1958 String groupId, String pkg, int uid) {
1959 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1960 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1964 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1965 checkCallerIsSystemOrSameApp(pkg);
1966 return mRankingHelper.getNotificationChannels(
1967 pkg, Binder.getCallingUid(), false /* includeDeleted */);
1971 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1972 checkCallerIsSystem();
1974 // Cancel posted notifications
1975 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1976 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1978 final String[] packages = new String[] {packageName};
1979 final int[] uids = new int[] {uid};
1981 // Listener & assistant
1982 mListeners.onPackagesChanged(true, packages, uids);
1983 mAssistants.onPackagesChanged(true, packages, uids);
1986 mConditionProviders.onPackagesChanged(true, packages, uids);
1988 // Reset notification preferences
1990 mRankingHelper.onPackagesChanged(
1991 true, UserHandle.getCallingUserId(), packages, uids);
1999 * System-only API for getting a list of current (i.e. not cleared) notifications.
2001 * Requires ACCESS_NOTIFICATIONS which is signature|system.
2002 * @returns A list of all the notifications, in natural order.
2005 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
2006 // enforce() will ensure the calling uid has the correct permission
2007 getContext().enforceCallingOrSelfPermission(
2008 android.Manifest.permission.ACCESS_NOTIFICATIONS,
2009 "NotificationManagerService.getActiveNotifications");
2011 StatusBarNotification[] tmp = null;
2012 int uid = Binder.getCallingUid();
2014 // noteOp will check to make sure the callingPkg matches the uid
2015 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
2016 == AppOpsManager.MODE_ALLOWED) {
2017 synchronized (mNotificationLock) {
2018 tmp = new StatusBarNotification[mNotificationList.size()];
2019 final int N = mNotificationList.size();
2020 for (int i=0; i<N; i++) {
2021 tmp[i] = mNotificationList.get(i).sbn;
2029 * Public API for getting a list of current notifications for the calling package/uid.
2031 * Note that since notification posting is done asynchronously, this will not return
2032 * notifications that are in the process of being posted.
2034 * @returns A list of all the package's notifications, in natural order.
2037 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
2038 int incomingUserId) {
2039 checkCallerIsSystemOrSameApp(pkg);
2040 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2041 Binder.getCallingUid(), incomingUserId, true, false,
2042 "getAppActiveNotifications", pkg);
2043 synchronized (mNotificationLock) {
2044 final ArrayMap<String, StatusBarNotification> map
2045 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
2046 final int N = mNotificationList.size();
2047 for (int i = 0; i < N; i++) {
2048 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2049 mNotificationList.get(i).sbn);
2051 map.put(sbn.getKey(), sbn);
2054 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
2055 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
2057 map.put(sbn.getKey(), sbn);
2060 final int M = mEnqueuedNotifications.size();
2061 for (int i = 0; i < M; i++) {
2062 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2063 mEnqueuedNotifications.get(i).sbn);
2065 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
2068 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
2069 list.addAll(map.values());
2070 return new ParceledListSlice<StatusBarNotification>(list);
2074 private StatusBarNotification sanitizeSbn(String pkg, int userId,
2075 StatusBarNotification sbn) {
2076 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
2077 // We could pass back a cloneLight() but clients might get confused and
2078 // try to send this thing back to notify() again, which would not work
2080 return new StatusBarNotification(
2081 sbn.getPackageName(),
2083 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
2084 sbn.getNotification().clone(),
2085 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
2091 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
2093 * Requires ACCESS_NOTIFICATIONS which is signature|system.
2096 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
2097 // enforce() will ensure the calling uid has the correct permission
2098 getContext().enforceCallingOrSelfPermission(
2099 android.Manifest.permission.ACCESS_NOTIFICATIONS,
2100 "NotificationManagerService.getHistoricalNotifications");
2102 StatusBarNotification[] tmp = null;
2103 int uid = Binder.getCallingUid();
2105 // noteOp will check to make sure the callingPkg matches the uid
2106 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
2107 == AppOpsManager.MODE_ALLOWED) {
2108 synchronized (mArchive) {
2109 tmp = mArchive.getArray(count);
2116 * Register a listener binder directly with the notification manager.
2118 * Only works with system callers. Apps should extend
2119 * {@link android.service.notification.NotificationListenerService}.
2122 public void registerListener(final INotificationListener listener,
2123 final ComponentName component, final int userid) {
2124 enforceSystemOrSystemUI("INotificationManager.registerListener");
2125 mListeners.registerService(listener, component, userid);
2129 * Remove a listener binder directly
2132 public void unregisterListener(INotificationListener token, int userid) {
2133 mListeners.unregisterService(token, userid);
2137 * Allow an INotificationListener to simulate a "clear all" operation.
2139 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2141 * @param token The binder for the listener, to check that the caller is allowed
2144 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
2145 final int callingUid = Binder.getCallingUid();
2146 final int callingPid = Binder.getCallingPid();
2147 long identity = Binder.clearCallingIdentity();
2149 synchronized (mNotificationLock) {
2150 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2152 final int N = keys.length;
2153 for (int i = 0; i < N; i++) {
2154 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2155 if (r == null) continue;
2156 final int userId = r.sbn.getUserId();
2157 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2158 !mUserProfiles.isCurrentProfile(userId)) {
2159 throw new SecurityException("Disallowed call from listener: "
2162 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2163 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2167 cancelAllLocked(callingUid, callingPid, info.userid,
2168 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
2172 Binder.restoreCallingIdentity(identity);
2177 * Handle request from an approved listener to re-enable itself.
2179 * @param component The componenet to be re-enabled, caller must match package.
2182 public void requestBindListener(ComponentName component) {
2183 checkCallerIsSystemOrSameApp(component.getPackageName());
2184 long identity = Binder.clearCallingIdentity();
2186 ManagedServices manager =
2187 mAssistants.isComponentEnabledForCurrentProfiles(component)
2190 manager.setComponentState(component, true);
2192 Binder.restoreCallingIdentity(identity);
2197 public void requestUnbindListener(INotificationListener token) {
2198 long identity = Binder.clearCallingIdentity();
2200 // allow bound services to disable themselves
2201 synchronized (mNotificationLock) {
2202 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2203 info.getOwner().setComponentState(info.component, false);
2206 Binder.restoreCallingIdentity(identity);
2211 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2212 long identity = Binder.clearCallingIdentity();
2214 synchronized (mNotificationLock) {
2215 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2217 final int N = keys.length;
2218 for (int i = 0; i < N; i++) {
2219 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2220 if (r == null) continue;
2221 final int userId = r.sbn.getUserId();
2222 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2223 !mUserProfiles.isCurrentProfile(userId)) {
2224 throw new SecurityException("Disallowed call from listener: "
2228 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2229 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2230 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2232 UsageEvents.Event.USER_INTERACTION);
2239 Binder.restoreCallingIdentity(identity);
2244 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2246 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2248 * @param info The binder for the listener, to check that the caller is allowed
2250 @GuardedBy("mNotificationLock")
2251 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2252 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2253 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2254 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2256 userId, REASON_LISTENER_CANCEL, info);
2260 * Allow an INotificationListener to snooze a single notification until a context.
2262 * @param token The binder for the listener, to check that the caller is allowed
2265 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2266 String key, String snoozeCriterionId) {
2267 long identity = Binder.clearCallingIdentity();
2269 synchronized (mNotificationLock) {
2270 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2271 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2274 Binder.restoreCallingIdentity(identity);
2279 * Allow an INotificationListener to snooze a single notification until a time.
2281 * @param token The binder for the listener, to check that the caller is allowed
2284 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2286 long identity = Binder.clearCallingIdentity();
2288 synchronized (mNotificationLock) {
2289 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2290 snoozeNotificationInt(key, duration, null, info);
2293 Binder.restoreCallingIdentity(identity);
2298 * Allows the notification assistant to un-snooze a single notification.
2300 * @param token The binder for the assistant, to check that the caller is allowed
2303 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2304 long identity = Binder.clearCallingIdentity();
2306 synchronized (mNotificationLock) {
2307 final ManagedServiceInfo info =
2308 mAssistants.checkServiceTokenLocked(token);
2309 unsnoozeNotificationInt(key, info);
2312 Binder.restoreCallingIdentity(identity);
2317 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2319 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2321 * @param token The binder for the listener, to check that the caller is allowed
2324 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2325 String tag, int id) {
2326 final int callingUid = Binder.getCallingUid();
2327 final int callingPid = Binder.getCallingPid();
2328 long identity = Binder.clearCallingIdentity();
2330 synchronized (mNotificationLock) {
2331 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2332 if (info.supportsProfiles()) {
2333 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2334 + "from " + info.component
2335 + " use cancelNotification(key) instead.");
2337 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2338 pkg, tag, id, info.userid);
2342 Binder.restoreCallingIdentity(identity);
2347 * Allow an INotificationListener to request the list of outstanding notifications seen by
2348 * the current user. Useful when starting up, after which point the listener callbacks
2351 * @param token The binder for the listener, to check that the caller is allowed
2352 * @param keys An array of notification keys to fetch, or null to fetch everything
2353 * @returns The return value will contain the notifications specified in keys, in that
2354 * order, or if keys is null, all the notifications, in natural order.
2357 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2358 INotificationListener token, String[] keys, int trim) {
2359 synchronized (mNotificationLock) {
2360 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2361 final boolean getKeys = keys != null;
2362 final int N = getKeys ? keys.length : mNotificationList.size();
2363 final ArrayList<StatusBarNotification> list
2364 = new ArrayList<StatusBarNotification>(N);
2365 for (int i=0; i<N; i++) {
2366 final NotificationRecord r = getKeys
2367 ? mNotificationsByKey.get(keys[i])
2368 : mNotificationList.get(i);
2369 if (r == null) continue;
2370 StatusBarNotification sbn = r.sbn;
2371 if (!isVisibleToListener(sbn, info)) continue;
2372 StatusBarNotification sbnToSend =
2373 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2374 list.add(sbnToSend);
2376 return new ParceledListSlice<StatusBarNotification>(list);
2381 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2382 * seen by the current user. Useful when starting up, after which point the listener
2383 * callbacks should be used.
2385 * @param token The binder for the listener, to check that the caller is allowed
2386 * @returns The return value will contain the notifications specified in keys, in that
2387 * order, or if keys is null, all the notifications, in natural order.
2390 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2391 INotificationListener token, int trim) {
2392 synchronized (mNotificationLock) {
2393 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2394 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2395 final int N = snoozedRecords.size();
2396 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2397 for (int i=0; i < N; i++) {
2398 final NotificationRecord r = snoozedRecords.get(i);
2399 if (r == null) continue;
2400 StatusBarNotification sbn = r.sbn;
2401 if (!isVisibleToListener(sbn, info)) continue;
2402 StatusBarNotification sbnToSend =
2403 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2404 list.add(sbnToSend);
2406 return new ParceledListSlice<>(list);
2411 public void requestHintsFromListener(INotificationListener token, int hints) {
2412 final long identity = Binder.clearCallingIdentity();
2414 synchronized (mNotificationLock) {
2415 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2416 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2417 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2418 | HINT_HOST_DISABLE_CALL_EFFECTS;
2419 final boolean disableEffects = (hints & disableEffectsMask) != 0;
2420 if (disableEffects) {
2421 addDisabledHints(info, hints);
2423 removeDisabledHints(info, hints);
2425 updateListenerHintsLocked();
2426 updateEffectsSuppressorLocked();
2429 Binder.restoreCallingIdentity(identity);
2434 public int getHintsFromListener(INotificationListener token) {
2435 synchronized (mNotificationLock) {
2436 return mListenerHints;
2441 public void requestInterruptionFilterFromListener(INotificationListener token,
2442 int interruptionFilter) throws RemoteException {
2443 final long identity = Binder.clearCallingIdentity();
2445 synchronized (mNotificationLock) {
2446 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2447 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2448 updateInterruptionFilterLocked();
2451 Binder.restoreCallingIdentity(identity);
2456 public int getInterruptionFilterFromListener(INotificationListener token)
2457 throws RemoteException {
2458 synchronized (mNotificationLight) {
2459 return mInterruptionFilter;
2464 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2465 throws RemoteException {
2466 synchronized (mNotificationLock) {
2467 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2468 if (info == null) return;
2469 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2474 public int getZenMode() {
2475 return mZenModeHelper.getZenMode();
2479 public ZenModeConfig getZenModeConfig() {
2480 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2481 return mZenModeHelper.getConfig();
2485 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2486 enforceSystemOrSystemUI("INotificationManager.setZenMode");
2487 final long identity = Binder.clearCallingIdentity();
2489 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2491 Binder.restoreCallingIdentity(identity);
2496 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2497 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2498 return mZenModeHelper.getZenRules();
2502 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2503 Preconditions.checkNotNull(id, "Id is null");
2504 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2505 return mZenModeHelper.getAutomaticZenRule(id);
2509 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2510 throws RemoteException {
2511 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2512 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2513 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2514 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2515 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2517 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2518 "addAutomaticZenRule");
2522 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2523 throws RemoteException {
2524 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2525 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2526 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2527 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2528 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2530 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2531 "updateAutomaticZenRule");
2535 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2536 Preconditions.checkNotNull(id, "Id is null");
2537 // Verify that they can modify zen rules.
2538 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2540 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2544 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2545 Preconditions.checkNotNull(packageName, "Package name is null");
2546 enforceSystemOrSystemUI("removeAutomaticZenRules");
2548 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2552 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2553 Preconditions.checkNotNull(owner, "Owner is null");
2554 enforceSystemOrSystemUI("getRuleInstanceCount");
2556 return mZenModeHelper.getCurrentInstanceCount(owner);
2560 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2561 enforcePolicyAccess(pkg, "setInterruptionFilter");
2562 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2563 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2564 final long identity = Binder.clearCallingIdentity();
2566 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2568 Binder.restoreCallingIdentity(identity);
2573 public void notifyConditions(final String pkg, IConditionProvider provider,
2574 final Condition[] conditions) {
2575 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2576 checkCallerIsSystemOrSameApp(pkg);
2577 mHandler.post(new Runnable() {
2580 mConditionProviders.notifyConditions(pkg, info, conditions);
2586 public void requestUnbindProvider(IConditionProvider provider) {
2587 long identity = Binder.clearCallingIdentity();
2589 // allow bound services to disable themselves
2590 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2591 info.getOwner().setComponentState(info.component, false);
2593 Binder.restoreCallingIdentity(identity);
2598 public void requestBindProvider(ComponentName component) {
2599 checkCallerIsSystemOrSameApp(component.getPackageName());
2600 long identity = Binder.clearCallingIdentity();
2602 mConditionProviders.setComponentState(component, true);
2604 Binder.restoreCallingIdentity(identity);
2608 private void enforceSystemOrSystemUI(String message) {
2609 if (isCallerSystemOrPhone()) return;
2610 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2614 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2616 checkCallerIsSystemOrSameApp(pkg);
2617 } catch (SecurityException e) {
2618 getContext().enforceCallingPermission(
2619 android.Manifest.permission.STATUS_BAR_SERVICE,
2624 private void enforcePolicyAccess(int uid, String method) {
2625 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2626 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2629 boolean accessAllowed = false;
2630 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2631 final int packageCount = packages.length;
2632 for (int i = 0; i < packageCount; i++) {
2633 if (mConditionProviders.isPackageOrComponentAllowed(
2634 packages[i], UserHandle.getUserId(uid))) {
2635 accessAllowed = true;
2638 if (!accessAllowed) {
2639 Slog.w(TAG, "Notification policy access denied calling " + method);
2640 throw new SecurityException("Notification policy access denied");
2644 private void enforcePolicyAccess(String pkg, String method) {
2645 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2646 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2649 checkCallerIsSameApp(pkg);
2650 if (!checkPolicyAccess(pkg)) {
2651 Slog.w(TAG, "Notification policy access denied calling " + method);
2652 throw new SecurityException("Notification policy access denied");
2656 private boolean checkPackagePolicyAccess(String pkg) {
2657 return mConditionProviders.isPackageOrComponentAllowed(
2658 pkg, getCallingUserHandle().getIdentifier());
2661 private boolean checkPolicyAccess(String pkg) {
2663 int uid = getContext().getPackageManager().getPackageUidAsUser(
2664 pkg, UserHandle.getCallingUserId());
2665 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2666 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2670 } catch (NameNotFoundException e) {
2673 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2677 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2678 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2679 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2680 if (filter != null && filter.stats) {
2681 dumpJson(pw, filter);
2682 } else if (filter != null && filter.proto) {
2683 dumpProto(fd, filter);
2685 dumpImpl(pw, filter);
2690 public ComponentName getEffectsSuppressor() {
2691 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2695 public boolean matchesCallFilter(Bundle extras) {
2696 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2697 return mZenModeHelper.matchesCallFilter(
2698 Binder.getCallingUserHandle(),
2700 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2701 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2702 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2706 public boolean isSystemConditionProviderEnabled(String path) {
2707 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2708 return mConditionProviders.isSystemProviderEnabled(path);
2711 // Backup/restore interface
2713 public byte[] getBackupPayload(int user) {
2714 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2715 //TODO: http://b/22388012
2716 if (user != UserHandle.USER_SYSTEM) {
2717 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2720 synchronized(mPolicyFile) {
2721 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2723 writePolicyXml(baos, true /*forBackup*/);
2724 return baos.toByteArray();
2725 } catch (IOException e) {
2726 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2733 public void applyRestore(byte[] payload, int user) {
2734 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2735 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2736 if (payload == null) {
2737 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2740 //TODO: http://b/22388012
2741 if (user != UserHandle.USER_SYSTEM) {
2742 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2745 synchronized(mPolicyFile) {
2746 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2748 readPolicyXml(bais, true /*forRestore*/);
2750 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2751 Slog.w(TAG, "applyRestore: error reading payload", e);
2757 public boolean isNotificationPolicyAccessGranted(String pkg) {
2758 return checkPolicyAccess(pkg);
2762 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2763 enforceSystemOrSystemUIOrSamePackage(pkg,
2764 "request policy access status for another package");
2765 return checkPolicyAccess(pkg);
2769 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2770 throws RemoteException {
2771 setNotificationPolicyAccessGrantedForUser(
2772 pkg, getCallingUserHandle().getIdentifier(), granted);
2776 public void setNotificationPolicyAccessGrantedForUser(
2777 String pkg, int userId, boolean granted) {
2778 checkCallerIsSystemOrShell();
2779 final long identity = Binder.clearCallingIdentity();
2781 if (!mActivityManager.isLowRamDevice()) {
2782 mConditionProviders.setPackageOrComponentEnabled(
2783 pkg, userId, true, granted);
2785 getContext().sendBroadcastAsUser(new Intent(
2786 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2788 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2789 UserHandle.of(userId), null);
2793 Binder.restoreCallingIdentity(identity);
2798 public Policy getNotificationPolicy(String pkg) {
2799 enforcePolicyAccess(pkg, "getNotificationPolicy");
2800 final long identity = Binder.clearCallingIdentity();
2802 return mZenModeHelper.getNotificationPolicy();
2804 Binder.restoreCallingIdentity(identity);
2809 public void setNotificationPolicy(String pkg, Policy policy) {
2810 enforcePolicyAccess(pkg, "setNotificationPolicy");
2811 final long identity = Binder.clearCallingIdentity();
2813 mZenModeHelper.setNotificationPolicy(policy);
2815 Binder.restoreCallingIdentity(identity);
2820 public List<String> getEnabledNotificationListenerPackages() {
2821 checkCallerIsSystem();
2822 return mListeners.getAllowedPackages(getCallingUserHandle().getIdentifier());
2826 public List<ComponentName> getEnabledNotificationListeners(int userId) {
2827 checkCallerIsSystem();
2828 return mListeners.getAllowedComponents(userId);
2832 public boolean isNotificationListenerAccessGranted(ComponentName listener) {
2833 Preconditions.checkNotNull(listener);
2834 checkCallerIsSystemOrSameApp(listener.getPackageName());
2835 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2836 getCallingUserHandle().getIdentifier());
2840 public boolean isNotificationListenerAccessGrantedForUser(ComponentName listener,
2842 Preconditions.checkNotNull(listener);
2843 checkCallerIsSystem();
2844 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2849 public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
2850 Preconditions.checkNotNull(assistant);
2851 checkCallerIsSystemOrSameApp(assistant.getPackageName());
2852 return mAssistants.isPackageOrComponentAllowed(assistant.flattenToString(),
2853 getCallingUserHandle().getIdentifier());
2857 public void setNotificationListenerAccessGranted(ComponentName listener,
2858 boolean granted) throws RemoteException {
2859 setNotificationListenerAccessGrantedForUser(
2860 listener, getCallingUserHandle().getIdentifier(), granted);
2864 public void setNotificationAssistantAccessGranted(ComponentName assistant,
2865 boolean granted) throws RemoteException {
2866 setNotificationAssistantAccessGrantedForUser(
2867 assistant, getCallingUserHandle().getIdentifier(), granted);
2871 public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
2872 boolean granted) throws RemoteException {
2873 Preconditions.checkNotNull(listener);
2874 checkCallerIsSystemOrShell();
2875 final long identity = Binder.clearCallingIdentity();
2877 if (!mActivityManager.isLowRamDevice()) {
2878 mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
2879 userId, false, granted);
2880 mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
2881 userId, true, granted);
2883 getContext().sendBroadcastAsUser(new Intent(
2884 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2885 .setPackage(listener.getPackageName())
2886 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2887 UserHandle.of(userId), null);
2892 Binder.restoreCallingIdentity(identity);
2897 public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
2898 int userId, boolean granted) throws RemoteException {
2899 Preconditions.checkNotNull(assistant);
2900 checkCallerIsSystemOrShell();
2901 final long identity = Binder.clearCallingIdentity();
2903 if (!mActivityManager.isLowRamDevice()) {
2904 mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
2905 userId, false, granted);
2906 mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
2907 userId, true, granted);
2909 getContext().sendBroadcastAsUser(new Intent(
2910 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2911 .setPackage(assistant.getPackageName())
2912 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2913 UserHandle.of(userId), null);
2918 Binder.restoreCallingIdentity(identity);
2923 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2924 Adjustment adjustment) throws RemoteException {
2925 final long identity = Binder.clearCallingIdentity();
2927 synchronized (mNotificationLock) {
2928 mAssistants.checkServiceTokenLocked(token);
2929 int N = mEnqueuedNotifications.size();
2930 for (int i = 0; i < N; i++) {
2931 final NotificationRecord n = mEnqueuedNotifications.get(i);
2932 if (Objects.equals(adjustment.getKey(), n.getKey())
2933 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2934 applyAdjustment(n, adjustment);
2940 Binder.restoreCallingIdentity(identity);
2945 public void applyAdjustmentFromAssistant(INotificationListener token,
2946 Adjustment adjustment) throws RemoteException {
2947 final long identity = Binder.clearCallingIdentity();
2949 synchronized (mNotificationLock) {
2950 mAssistants.checkServiceTokenLocked(token);
2951 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2952 applyAdjustment(n, adjustment);
2954 mRankingHandler.requestSort();
2956 Binder.restoreCallingIdentity(identity);
2961 public void applyAdjustmentsFromAssistant(INotificationListener token,
2962 List<Adjustment> adjustments) throws RemoteException {
2964 final long identity = Binder.clearCallingIdentity();
2966 synchronized (mNotificationLock) {
2967 mAssistants.checkServiceTokenLocked(token);
2968 for (Adjustment adjustment : adjustments) {
2969 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2970 applyAdjustment(n, adjustment);
2973 mRankingHandler.requestSort();
2975 Binder.restoreCallingIdentity(identity);
2980 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2981 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2982 Preconditions.checkNotNull(channel);
2983 Preconditions.checkNotNull(pkg);
2984 Preconditions.checkNotNull(user);
2986 verifyPrivilegedListener(token, user);
2987 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2991 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2992 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2993 Preconditions.checkNotNull(pkg);
2994 Preconditions.checkNotNull(user);
2995 verifyPrivilegedListener(token, user);
2997 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2998 false /* includeDeleted */);
3002 public ParceledListSlice<NotificationChannelGroup>
3003 getNotificationChannelGroupsFromPrivilegedListener(
3004 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
3005 Preconditions.checkNotNull(pkg);
3006 Preconditions.checkNotNull(user);
3007 verifyPrivilegedListener(token, user);
3009 List<NotificationChannelGroup> groups = new ArrayList<>();
3010 groups.addAll(mRankingHelper.getNotificationChannelGroups(
3011 pkg, getUidForPackageAndUser(pkg, user)));
3012 return new ParceledListSlice<>(groups);
3015 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
3016 ManagedServiceInfo info;
3017 synchronized (mNotificationLock) {
3018 info = mListeners.checkServiceTokenLocked(token);
3020 if (!hasCompanionDevice(info)) {
3021 throw new SecurityException(info + " does not have access");
3023 if (!info.enabledAndUserMatches(user.getIdentifier())) {
3024 throw new SecurityException(info + " does not have access");
3028 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
3030 long identity = Binder.clearCallingIdentity();
3032 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
3034 Binder.restoreCallingIdentity(identity);
3040 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
3041 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
3042 throws RemoteException {
3043 new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
3047 private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
3051 if (adjustment.getSignals() != null) {
3052 Bundle.setDefusable(adjustment.getSignals(), true);
3053 r.addAdjustment(adjustment);
3057 @GuardedBy("mNotificationLock")
3058 void addAutogroupKeyLocked(String key) {
3059 NotificationRecord r = mNotificationsByKey.get(key);
3063 if (r.sbn.getOverrideGroupKey() == null) {
3064 addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
3065 EventLogTags.writeNotificationAutogrouped(key);
3066 mRankingHandler.requestSort();
3070 @GuardedBy("mNotificationLock")
3071 void removeAutogroupKeyLocked(String key) {
3072 NotificationRecord r = mNotificationsByKey.get(key);
3076 if (r.sbn.getOverrideGroupKey() != null) {
3077 addAutoGroupAdjustment(r, null);
3078 EventLogTags.writeNotificationUnautogrouped(key);
3079 mRankingHandler.requestSort();
3083 private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
3084 Bundle signals = new Bundle();
3085 signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
3086 Adjustment adjustment =
3087 new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
3088 r.addAdjustment(adjustment);
3091 // Clears the 'fake' auto-group summary.
3092 @GuardedBy("mNotificationLock")
3093 private void clearAutogroupSummaryLocked(int userId, String pkg) {
3094 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3095 if (summaries != null && summaries.containsKey(pkg)) {
3097 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
3098 if (removed != null) {
3099 boolean wasPosted = removeFromNotificationListsLocked(removed);
3100 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted, null);
3105 @GuardedBy("mNotificationLock")
3106 private boolean hasAutoGroupSummaryLocked(StatusBarNotification sbn) {
3107 ArrayMap<String, String> summaries = mAutobundledSummaries.get(sbn.getUserId());
3108 return summaries != null && summaries.containsKey(sbn.getPackageName());
3111 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
3112 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
3113 NotificationRecord summaryRecord = null;
3114 synchronized (mNotificationLock) {
3115 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
3116 if (notificationRecord == null) {
3117 // The notification could have been cancelled again already. A successive
3118 // adjustment will post a summary if needed.
3121 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
3122 userId = adjustedSbn.getUser().getIdentifier();
3123 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3124 if (summaries == null) {
3125 summaries = new ArrayMap<>();
3127 mAutobundledSummaries.put(userId, summaries);
3128 if (!summaries.containsKey(pkg)) {
3130 final ApplicationInfo appInfo =
3131 adjustedSbn.getNotification().extras.getParcelable(
3132 Notification.EXTRA_BUILDER_APPLICATION_INFO);
3133 final Bundle extras = new Bundle();
3134 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
3135 final String channelId = notificationRecord.getChannel().getId();
3136 final Notification summaryNotification =
3137 new Notification.Builder(getContext(), channelId)
3138 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
3139 .setGroupSummary(true)
3140 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
3141 .setGroup(GroupHelper.AUTOGROUP_KEY)
3142 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
3143 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
3144 .setColor(adjustedSbn.getNotification().color)
3147 summaryNotification.extras.putAll(extras);
3148 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
3149 if (appIntent != null) {
3150 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
3151 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
3153 final StatusBarNotification summarySbn =
3154 new StatusBarNotification(adjustedSbn.getPackageName(),
3155 adjustedSbn.getOpPkg(),
3157 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
3158 adjustedSbn.getInitialPid(), summaryNotification,
3159 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
3160 System.currentTimeMillis());
3161 summaryRecord = new NotificationRecord(getContext(), summarySbn,
3162 notificationRecord.getChannel());
3163 summaries.put(pkg, summarySbn.getKey());
3166 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
3167 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord, true)) {
3168 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
3172 private String disableNotificationEffects(NotificationRecord record) {
3173 if (mDisableNotificationEffects) {
3174 return "booleanState";
3176 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
3177 return "listenerHints";
3179 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
3185 private void dumpJson(PrintWriter pw, DumpFilter filter) {
3186 JSONObject dump = new JSONObject();
3188 dump.put("service", "Notification Manager");
3189 dump.put("bans", mRankingHelper.dumpBansJson(filter));
3190 dump.put("ranking", mRankingHelper.dumpJson(filter));
3191 dump.put("stats", mUsageStats.dumpJson(filter));
3192 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
3193 } catch (JSONException e) {
3194 e.printStackTrace();
3199 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
3200 final ProtoOutputStream proto = new ProtoOutputStream(fd);
3201 synchronized (mNotificationLock) {
3202 long records = proto.start(NotificationServiceDumpProto.RECORDS);
3203 int N = mNotificationList.size();
3205 for (int i = 0; i < N; i++) {
3206 final NotificationRecord nr = mNotificationList.get(i);
3207 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3208 nr.dump(proto, filter.redact);
3209 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
3212 N = mEnqueuedNotifications.size();
3214 for (int i = 0; i < N; i++) {
3215 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3216 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3217 nr.dump(proto, filter.redact);
3218 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
3221 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
3224 for (int i = 0; i < N; i++) {
3225 final NotificationRecord nr = snoozed.get(i);
3226 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3227 nr.dump(proto, filter.redact);
3228 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
3234 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
3235 mZenModeHelper.dump(proto);
3236 for (ComponentName suppressor : mEffectsSuppressors) {
3237 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
3244 void dumpImpl(PrintWriter pw, DumpFilter filter) {
3245 pw.print("Current Notification Manager state");
3246 if (filter.filtered) {
3247 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
3251 final boolean zenOnly = filter.filtered && filter.zen;
3254 synchronized (mToastQueue) {
3255 N = mToastQueue.size();
3257 pw.println(" Toast Queue:");
3258 for (int i=0; i<N; i++) {
3259 mToastQueue.get(i).dump(pw, " ", filter);
3266 synchronized (mNotificationLock) {
3268 N = mNotificationList.size();
3270 pw.println(" Notification List:");
3271 for (int i=0; i<N; i++) {
3272 final NotificationRecord nr = mNotificationList.get(i);
3273 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3274 nr.dump(pw, " ", getContext(), filter.redact);
3279 if (!filter.filtered) {
3282 pw.println(" Lights List:");
3283 for (int i=0; i<N; i++) {
3289 pw.println(mLights.get(i));
3293 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3294 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
3295 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3296 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
3297 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
3298 pw.println(" mCallState=" + callStateToString(mCallState));
3299 pw.println(" mSystemReady=" + mSystemReady);
3300 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
3302 pw.println(" mArchive=" + mArchive.toString());
3303 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
3305 while (iter.hasNext()) {
3306 final StatusBarNotification sbn = iter.next();
3307 if (filter != null && !filter.matches(sbn)) continue;
3308 pw.println(" " + sbn);
3310 if (iter.hasNext()) pw.println(" ...");
3316 N = mEnqueuedNotifications.size();
3318 pw.println(" Enqueued Notification List:");
3319 for (int i = 0; i < N; i++) {
3320 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3321 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3322 nr.dump(pw, " ", getContext(), filter.redact);
3327 mSnoozeHelper.dump(pw, filter);
3332 pw.println("\n Ranking Config:");
3333 mRankingHelper.dump(pw, " ", filter);
3335 pw.println("\n Notification listeners:");
3336 mListeners.dump(pw, filter);
3337 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3338 pw.print(" mListenersDisablingEffects: (");
3339 N = mListenersDisablingEffects.size();
3340 for (int i = 0; i < N; i++) {
3341 final int hint = mListenersDisablingEffects.keyAt(i);
3342 if (i > 0) pw.print(';');
3343 pw.print("hint[" + hint + "]:");
3345 final ArraySet<ManagedServiceInfo> listeners =
3346 mListenersDisablingEffects.valueAt(i);
3347 final int listenerSize = listeners.size();
3349 for (int j = 0; j < listenerSize; j++) {
3350 if (i > 0) pw.print(',');
3351 final ManagedServiceInfo listener = listeners.valueAt(i);
3352 pw.print(listener.component);
3356 pw.println("\n Notification assistant services:");
3357 mAssistants.dump(pw, filter);
3360 if (!filter.filtered || zenOnly) {
3361 pw.println("\n Zen Mode:");
3362 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3363 mZenModeHelper.dump(pw, " ");
3365 pw.println("\n Zen Log:");
3366 ZenLog.dump(pw, " ");
3369 pw.println("\n Condition providers:");
3370 mConditionProviders.dump(pw, filter);
3372 pw.println("\n Group summaries:");
3373 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3374 NotificationRecord r = entry.getValue();
3375 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3376 if (mNotificationsByKey.get(r.getKey()) != r) {
3377 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3378 r.dump(pw, " ", getContext(), filter.redact);
3383 pw.println("\n Usage Stats:");
3384 mUsageStats.dump(pw, " ", filter);
3390 * The private API only accessible to the system process.
3392 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3394 public NotificationChannel getNotificationChannel(String pkg, int uid, String
3396 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
3400 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3401 String tag, int id, Notification notification, int userId) {
3402 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3407 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3409 checkCallerIsSystem();
3410 mHandler.post(new Runnable() {
3413 synchronized (mNotificationLock) {
3414 removeForegroundServiceFlagByListLocked(
3415 mEnqueuedNotifications, pkg, notificationId, userId);
3416 removeForegroundServiceFlagByListLocked(
3417 mNotificationList, pkg, notificationId, userId);
3423 @GuardedBy("mNotificationLock")
3424 private void removeForegroundServiceFlagByListLocked(
3425 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3427 NotificationRecord r = findNotificationByListLocked(
3428 notificationList, pkg, null, notificationId, userId);
3432 StatusBarNotification sbn = r.sbn;
3433 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3434 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3435 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3436 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3437 sbn.getNotification().flags =
3438 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3439 mRankingHelper.sort(mNotificationList);
3440 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3444 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3445 final int callingPid, final String tag, final int id, final Notification notification,
3446 int incomingUserId) {
3448 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3449 + " notification=" + notification);
3451 checkCallerIsSystemOrSameApp(pkg);
3453 final int userId = ActivityManager.handleIncomingUser(callingPid,
3454 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3455 final UserHandle user = new UserHandle(userId);
3457 if (pkg == null || notification == null) {
3458 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3459 + " id=" + id + " notification=" + notification);
3462 // The system can post notifications for any package, let us resolve that.
3463 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3465 // Fix the notification as best we can.
3467 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3468 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3469 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3470 Notification.addFieldsFromContext(ai, notification);
3472 int canColorize = mPackageManagerClient.checkPermission(
3473 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
3474 if (canColorize == PERMISSION_GRANTED) {
3475 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3477 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3480 } catch (NameNotFoundException e) {
3481 Slog.e(TAG, "Cannot create a context for sending app", e);
3485 mUsageStats.registerEnqueuedByApp(pkg);
3487 // setup local book-keeping
3488 String channelId = notification.getChannelId();
3489 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3490 channelId = (new Notification.TvExtender(notification)).getChannelId();
3492 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3493 notificationUid, channelId, false /* includeDeleted */);
3494 if (channel == null) {
3495 final String noChannelStr = "No Channel found for "
3497 + ", channelId=" + channelId
3500 + ", opPkg=" + opPkg
3501 + ", callingUid=" + callingUid
3502 + ", userId=" + userId
3503 + ", incomingUserId=" + incomingUserId
3504 + ", notificationUid=" + notificationUid
3505 + ", notification=" + notification;
3506 Log.e(TAG, noChannelStr);
3507 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3508 "Failed to post notification on channel \"" + channelId + "\"\n" +
3509 "See log for more details");
3513 final StatusBarNotification n = new StatusBarNotification(
3514 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3515 user, null, System.currentTimeMillis());
3516 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3518 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
3519 && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
3520 && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
3521 // Increase the importance of foreground service notifications unless the user had an
3522 // opinion otherwise
3523 if (TextUtils.isEmpty(channelId)
3524 || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
3525 r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service");
3527 channel.setImportance(IMPORTANCE_LOW);
3528 mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
3529 r.updateNotificationChannel(channel);
3533 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
3534 r.sbn.getOverrideGroupKey() != null)) {
3538 // Whitelist pending intents.
3539 if (notification.allPendingIntents != null) {
3540 final int intentCount = notification.allPendingIntents.size();
3541 if (intentCount > 0) {
3542 final ActivityManagerInternal am = LocalServices
3543 .getService(ActivityManagerInternal.class);
3544 final long duration = LocalServices.getService(
3545 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3546 for (int i = 0; i < intentCount; i++) {
3547 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3548 if (pendingIntent != null) {
3549 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3550 WHITELIST_TOKEN, duration);
3556 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3559 private void doChannelWarningToast(CharSequence toastText) {
3560 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3561 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3562 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3563 if (warningEnabled) {
3564 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3565 Toast.LENGTH_SHORT);
3570 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3571 // The system can post notifications on behalf of any package it wants
3572 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3574 return getContext().getPackageManager()
3575 .getPackageUidAsUser(opPackageName, userId);
3576 } catch (NameNotFoundException e) {
3584 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3588 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3589 NotificationRecord r, boolean isAutogroup) {
3590 final String pkg = r.sbn.getPackageName();
3591 final String dialerPackage =
3592 getContext().getSystemService(TelecomManager.class).getSystemDialerPackage();
3593 final boolean isSystemNotification =
3594 isUidSystemOrPhone(callingUid) || ("android".equals(pkg))
3595 || TextUtils.equals(pkg, dialerPackage);
3596 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3598 // Limit the number of notifications that any given package except the android
3599 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3600 if (!isSystemNotification && !isNotificationFromListener) {
3601 synchronized (mNotificationLock) {
3602 if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
3603 // Ephemeral apps have some special constraints for notifications.
3604 // They are not allowed to create new notifications however they are allowed to
3605 // update notifications created by the system (e.g. a foreground service
3607 throw new SecurityException("Instant app " + pkg
3608 + " cannot create notifications");
3611 // rate limit updates that aren't completed progress notifications
3612 if (mNotificationsByKey.get(r.sbn.getKey()) != null
3613 && !r.getNotification().hasCompletedProgress()
3616 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3617 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3618 mUsageStats.registerOverRateQuota(pkg);
3619 final long now = SystemClock.elapsedRealtime();
3620 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3621 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3622 + ". Shedding " + r.sbn.getKey() + ". package=" + pkg);
3623 mLastOverRateLogTime = now;
3629 // limit the number of outstanding notificationrecords an app can have
3630 int count = getNotificationCountLocked(pkg, userId, id, tag);
3631 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3632 mUsageStats.registerOverCountQuota(pkg);
3633 Slog.e(TAG, "Package has already posted or enqueued " + count
3634 + " notifications. Not showing more. package=" + pkg);
3641 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3642 MetricsLogger.action(r.getLogMaker()
3643 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3644 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3646 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3648 mSnoozeHelper.update(userId, r);
3655 if (isBlocked(r, mUsageStats)) {
3662 protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
3663 String excludedTag) {
3665 final int N = mNotificationList.size();
3666 for (int i = 0; i < N; i++) {
3667 final NotificationRecord existing = mNotificationList.get(i);
3668 if (existing.sbn.getPackageName().equals(pkg)
3669 && existing.sbn.getUserId() == userId) {
3670 if (existing.sbn.getId() == excludedId
3671 && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
3677 final int M = mEnqueuedNotifications.size();
3678 for (int i = 0; i < M; i++) {
3679 final NotificationRecord existing = mEnqueuedNotifications.get(i);
3680 if (existing.sbn.getPackageName().equals(pkg)
3681 && existing.sbn.getUserId() == userId) {
3688 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3689 final String pkg = r.sbn.getPackageName();
3690 final int callingUid = r.sbn.getUid();
3692 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3693 if (isPackageSuspended) {
3694 Slog.e(TAG, "Suppressing notification from package due to package "
3695 + "suspended by administrator.");
3696 usageStats.registerSuspendedByAdmin(r);
3697 return isPackageSuspended;
3700 final boolean isBlocked =
3701 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
3702 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3704 Slog.e(TAG, "Suppressing notification from package by user request.");
3705 usageStats.registerBlocked(r);
3710 protected class SnoozeNotificationRunnable implements Runnable {
3711 private final String mKey;
3712 private final long mDuration;
3713 private final String mSnoozeCriterionId;
3715 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3717 mDuration = duration;
3718 mSnoozeCriterionId = snoozeCriterionId;
3723 synchronized (mNotificationLock) {
3724 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3731 @GuardedBy("mNotificationLock")
3732 void snoozeLocked(NotificationRecord r) {
3733 if (r.sbn.isGroup()) {
3734 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3735 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3736 if (r.getNotification().isGroupSummary()) {
3737 // snooze summary and all children
3738 for (int i = 0; i < groupNotifications.size(); i++) {
3739 snoozeNotificationLocked(groupNotifications.get(i));
3742 // if there is a valid summary for this group, and we are snoozing the only
3743 // child, also snooze the summary
3744 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3745 if (groupNotifications.size() != 2) {
3746 snoozeNotificationLocked(r);
3748 // snooze summary and the one child
3749 for (int i = 0; i < groupNotifications.size(); i++) {
3750 snoozeNotificationLocked(groupNotifications.get(i));
3754 snoozeNotificationLocked(r);
3758 // just snooze the one notification
3759 snoozeNotificationLocked(r);
3763 @GuardedBy("mNotificationLock")
3764 void snoozeNotificationLocked(NotificationRecord r) {
3765 MetricsLogger.action(r.getLogMaker()
3766 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3767 .setType(MetricsEvent.TYPE_CLOSE)
3768 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS,
3770 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3771 mSnoozeCriterionId == null ? 0 : 1));
3772 boolean wasPosted = removeFromNotificationListsLocked(r);
3773 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
3774 updateLightsLocked();
3775 if (mSnoozeCriterionId != null) {
3776 mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3777 mSnoozeHelper.snooze(r);
3779 mSnoozeHelper.snooze(r, mDuration);
3785 protected class EnqueueNotificationRunnable implements Runnable {
3786 private final NotificationRecord r;
3787 private final int userId;
3789 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3790 this.userId = userId;
3796 synchronized (mNotificationLock) {
3797 mEnqueuedNotifications.add(r);
3798 scheduleTimeoutLocked(r);
3800 final StatusBarNotification n = r.sbn;
3801 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3802 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3804 // Retain ranking information from previous record
3805 r.copyRankingInformation(old);
3808 final int callingUid = n.getUid();
3809 final int callingPid = n.getInitialPid();
3810 final Notification notification = n.getNotification();
3811 final String pkg = n.getPackageName();
3812 final int id = n.getId();
3813 final String tag = n.getTag();
3815 // Handle grouped notifications and bail out early if we
3816 // can to avoid extracting signals.
3817 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3819 // if this is a group child, unsnooze parent summary
3820 if (n.isGroup() && notification.isGroupChild()) {
3821 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3824 // This conditional is a dirty hack to limit the logging done on
3825 // behalf of the download manager without affecting other apps.
3826 if (!pkg.equals("com.android.providers.downloads")
3827 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3828 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3830 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3832 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3833 pkg, id, tag, userId, notification.toString(),
3837 mRankingHelper.extractSignals(r);
3839 // tell the assistant service about the notification
3840 if (mAssistants.isEnabled()) {
3841 mAssistants.onNotificationEnqueued(r);
3842 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3843 DELAY_FOR_ASSISTANT_TIME);
3845 mHandler.post(new PostNotificationRunnable(r.getKey()));
3851 protected class PostNotificationRunnable implements Runnable {
3852 private final String key;
3854 PostNotificationRunnable(String key) {
3860 synchronized (mNotificationLock) {
3862 NotificationRecord r = null;
3863 int N = mEnqueuedNotifications.size();
3864 for (int i = 0; i < N; i++) {
3865 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3866 if (Objects.equals(key, enqueued.getKey())) {
3872 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3875 NotificationRecord old = mNotificationsByKey.get(key);
3876 final StatusBarNotification n = r.sbn;
3877 final Notification notification = n.getNotification();
3878 int index = indexOfNotificationLocked(n.getKey());
3880 mNotificationList.add(r);
3881 mUsageStats.registerPostedByApp(r);
3883 old = mNotificationList.get(index);
3884 mNotificationList.set(index, r);
3885 mUsageStats.registerUpdatedByApp(r, old);
3886 // Make sure we don't lose the foreground service state.
3887 notification.flags |=
3888 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3892 mNotificationsByKey.put(n.getKey(), r);
3894 // Ensure if this is a foreground service that the proper additional
3896 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3897 notification.flags |= Notification.FLAG_ONGOING_EVENT
3898 | Notification.FLAG_NO_CLEAR;
3901 applyZenModeLocked(r);
3902 mRankingHelper.sort(mNotificationList);
3904 if (notification.getSmallIcon() != null) {
3905 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3906 mListeners.notifyPostedLocked(n, oldSbn);
3907 if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
3908 mHandler.post(new Runnable() {
3911 mGroupHelper.onNotificationPosted(
3912 n, hasAutoGroupSummaryLocked(n));
3917 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3918 if (old != null && !old.isCanceled) {
3919 mListeners.notifyRemovedLocked(n,
3920 NotificationListenerService.REASON_ERROR);
3921 mHandler.post(new Runnable() {
3924 mGroupHelper.onNotificationRemoved(n);
3928 // ATTENTION: in a future release we will bail out here
3929 // so that we do not play sounds, show lights, etc. for invalid
3931 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3932 + n.getPackageName());
3935 buzzBeepBlinkLocked(r);
3937 int N = mEnqueuedNotifications.size();
3938 for (int i = 0; i < N; i++) {
3939 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3940 if (Objects.equals(key, enqueued.getKey())) {
3941 mEnqueuedNotifications.remove(i);
3951 * Ensures that grouped notification receive their special treatment.
3953 * <p>Cancels group children if the new notification causes a group to lose
3956 * <p>Updates mSummaryByGroupKey.</p>
3958 @GuardedBy("mNotificationLock")
3959 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3960 int callingUid, int callingPid) {
3961 StatusBarNotification sbn = r.sbn;
3962 Notification n = sbn.getNotification();
3963 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3964 // notifications without a group shouldn't be a summary, otherwise autobundling can
3966 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3969 String group = sbn.getGroupKey();
3970 boolean isSummary = n.isGroupSummary();
3972 Notification oldN = old != null ? old.sbn.getNotification() : null;
3973 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3974 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3977 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3978 if (removedSummary != old) {
3980 removedSummary != null ? removedSummary.getKey() : "<null>";
3981 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3982 ", removed=" + removedKey);
3986 mSummaryByGroupKey.put(group, r);
3989 // Clear out group children of the old notification if the update
3990 // causes the group summary to go away. This happens when the old
3991 // notification was a summary and the new one isn't, or when the old
3992 // notification was a summary and its group key changed.
3993 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3994 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
4000 @GuardedBy("mNotificationLock")
4001 void scheduleTimeoutLocked(NotificationRecord record) {
4002 if (record.getNotification().getTimeoutAfter() > 0) {
4003 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
4004 REQUEST_CODE_TIMEOUT,
4005 new Intent(ACTION_NOTIFICATION_TIMEOUT)
4006 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
4007 .appendPath(record.getKey()).build())
4008 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
4009 .putExtra(EXTRA_KEY, record.getKey()),
4010 PendingIntent.FLAG_UPDATE_CURRENT);
4011 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
4012 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
4017 @GuardedBy("mNotificationLock")
4018 void buzzBeepBlinkLocked(NotificationRecord record) {
4019 boolean buzz = false;
4020 boolean beep = false;
4021 boolean blink = false;
4023 final Notification notification = record.sbn.getNotification();
4024 final String key = record.getKey();
4026 // Should this notification make noise, vibe, or use the LED?
4027 final boolean aboveThreshold =
4028 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
4030 // Remember if this notification already owns the notification channels.
4031 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
4032 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
4033 // These are set inside the conditional if the notification is allowed to make noise.
4034 boolean hasValidVibrate = false;
4035 boolean hasValidSound = false;
4036 boolean sentAccessibilityEvent = false;
4037 // If the notification will appear in the status bar, it should send an accessibility
4039 if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
4040 sendAccessibilityEvent(notification, record.sbn.getPackageName());
4041 sentAccessibilityEvent = true;
4044 if (aboveThreshold && isNotificationForCurrentUser(record)) {
4046 if (mSystemReady && mAudioManager != null) {
4047 Uri soundUri = record.getSound();
4048 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
4049 long[] vibration = record.getVibration();
4050 // Demote sound to vibration if vibration missing & phone in vibration mode.
4051 if (vibration == null
4053 && (mAudioManager.getRingerModeInternal()
4054 == AudioManager.RINGER_MODE_VIBRATE)
4055 && mAudioManager.getStreamVolume(
4056 AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
4057 vibration = mFallbackVibrationPattern;
4059 hasValidVibrate = vibration != null;
4061 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
4062 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
4063 if (!sentAccessibilityEvent) {
4064 sendAccessibilityEvent(notification, record.sbn.getPackageName());
4065 sentAccessibilityEvent = true;
4067 if (DBG) Slog.v(TAG, "Interrupting!");
4068 if (hasValidSound) {
4069 mSoundNotificationKey = key;
4071 playInCallNotification();
4074 beep = playSound(record, soundUri);
4078 final boolean ringerModeSilent =
4079 mAudioManager.getRingerModeInternal()
4080 == AudioManager.RINGER_MODE_SILENT;
4081 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
4082 mVibrateNotificationKey = key;
4084 buzz = playVibration(record, vibration, hasValidSound);
4089 // If a notification is updated to remove the actively playing sound or vibrate,
4090 // cancel that feedback now
4091 if (wasBeep && !hasValidSound) {
4094 if (wasBuzz && !hasValidVibrate) {
4095 clearVibrateLocked();
4099 // release the light
4100 boolean wasShowLights = mLights.remove(key);
4101 if (record.getLight() != null && aboveThreshold
4102 && ((record.getSuppressedVisualEffects()
4103 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
4105 updateLightsLocked();
4106 if (mUseAttentionLight) {
4107 mAttentionLight.pulse();
4110 } else if (wasShowLights) {
4111 updateLightsLocked();
4113 if (buzz || beep || blink) {
4114 MetricsLogger.action(record.getLogMaker()
4115 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
4116 .setType(MetricsEvent.TYPE_OPEN)
4117 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
4118 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
4122 @GuardedBy("mNotificationLock")
4123 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
4124 // Suppressed because it's a silent update
4125 final Notification notification = record.getNotification();
4127 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
4131 // muted by listener
4132 final String disableEffects = disableNotificationEffects(record);
4133 if (disableEffects != null) {
4134 ZenLog.traceDisableEffects(record, disableEffects);
4138 // suppressed due to DND
4139 if (record.isIntercepted()) {
4143 // Suppressed because another notification in its group handles alerting
4144 if (record.sbn.isGroup()) {
4145 return notification.suppressAlertingDueToGrouping();
4148 // Suppressed for being too recently noisy
4149 final String pkg = record.sbn.getPackageName();
4150 if (mUsageStats.isAlertRateLimited(pkg)) {
4151 Slog.e(TAG, "Muting recently noisy " + record.getKey());
4158 private boolean playSound(final NotificationRecord record, Uri soundUri) {
4159 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4160 // do not play notifications if there is a user of exclusive audio focus
4161 // or the device is in vibrate mode
4162 if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
4163 != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
4164 AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
4165 final long identity = Binder.clearCallingIdentity();
4167 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4168 if (player != null) {
4169 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
4170 + " with attributes " + record.getAudioAttributes());
4171 player.playAsync(soundUri, record.sbn.getUser(), looping,
4172 record.getAudioAttributes());
4175 } catch (RemoteException e) {
4177 Binder.restoreCallingIdentity(identity);
4183 private boolean playVibration(final NotificationRecord record, long[] vibration,
4184 boolean delayVibForSound) {
4185 // Escalate privileges so we can use the vibrator even if the
4186 // notifying app does not have the VIBRATE permission.
4187 long identity = Binder.clearCallingIdentity();
4189 final VibrationEffect effect;
4191 final boolean insistent =
4192 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4193 effect = VibrationEffect.createWaveform(
4194 vibration, insistent ? 0 : -1 /*repeatIndex*/);
4195 } catch (IllegalArgumentException e) {
4196 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
4197 Arrays.toString(vibration));
4200 if (delayVibForSound) {
4202 // delay the vibration by the same amount as the notification sound
4203 final int waitMs = mAudioManager.getFocusRampTimeMs(
4204 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
4205 record.getAudioAttributes());
4206 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
4208 Thread.sleep(waitMs);
4209 } catch (InterruptedException e) { }
4210 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4211 effect, record.getAudioAttributes());
4214 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4215 effect, record.getAudioAttributes());
4219 Binder.restoreCallingIdentity(identity);
4223 private boolean isNotificationForCurrentUser(NotificationRecord record) {
4224 final int currentUser;
4225 final long token = Binder.clearCallingIdentity();
4227 currentUser = ActivityManager.getCurrentUser();
4229 Binder.restoreCallingIdentity(token);
4231 return (record.getUserId() == UserHandle.USER_ALL ||
4232 record.getUserId() == currentUser ||
4233 mUserProfiles.isCurrentProfile(record.getUserId()));
4236 protected void playInCallNotification() {
4240 final long identity = Binder.clearCallingIdentity();
4242 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4243 if (player != null) {
4244 player.play(new Binder(), mInCallNotificationUri,
4245 mInCallNotificationAudioAttributes,
4246 mInCallNotificationVolume, false);
4248 } catch (RemoteException e) {
4250 Binder.restoreCallingIdentity(identity);
4256 @GuardedBy("mToastQueue")
4257 void showNextToastLocked() {
4258 ToastRecord record = mToastQueue.get(0);
4259 while (record != null) {
4260 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
4262 record.callback.show(record.token);
4263 scheduleTimeoutLocked(record);
4265 } catch (RemoteException e) {
4266 Slog.w(TAG, "Object died trying to show notification " + record.callback
4267 + " in package " + record.pkg);
4268 // remove it from the list and let the process die
4269 int index = mToastQueue.indexOf(record);
4271 mToastQueue.remove(index);
4273 keepProcessAliveIfNeededLocked(record.pid);
4274 if (mToastQueue.size() > 0) {
4275 record = mToastQueue.get(0);
4283 @GuardedBy("mToastQueue")
4284 void cancelToastLocked(int index) {
4285 ToastRecord record = mToastQueue.get(index);
4287 record.callback.hide();
4288 } catch (RemoteException e) {
4289 Slog.w(TAG, "Object died trying to hide notification " + record.callback
4290 + " in package " + record.pkg);
4291 // don't worry about this, we're about to remove it from
4295 ToastRecord lastToast = mToastQueue.remove(index);
4296 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
4298 keepProcessAliveIfNeededLocked(record.pid);
4299 if (mToastQueue.size() > 0) {
4300 // Show the next one. If the callback fails, this will remove
4301 // it from the list, so don't assume that the list hasn't changed
4302 // after this point.
4303 showNextToastLocked();
4307 @GuardedBy("mToastQueue")
4308 private void scheduleTimeoutLocked(ToastRecord r)
4310 mHandler.removeCallbacksAndMessages(r);
4311 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
4312 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
4313 mHandler.sendMessageDelayed(m, delay);
4316 private void handleTimeout(ToastRecord record)
4318 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
4319 synchronized (mToastQueue) {
4320 int index = indexOfToastLocked(record.pkg, record.callback);
4322 cancelToastLocked(index);
4327 @GuardedBy("mToastQueue")
4328 int indexOfToastLocked(String pkg, ITransientNotification callback)
4330 IBinder cbak = callback.asBinder();
4331 ArrayList<ToastRecord> list = mToastQueue;
4332 int len = list.size();
4333 for (int i=0; i<len; i++) {
4334 ToastRecord r = list.get(i);
4335 if (r.pkg.equals(pkg) && r.callback.asBinder().equals(cbak)) {
4342 @GuardedBy("mToastQueue")
4343 int indexOfToastPackageLocked(String pkg)
4345 ArrayList<ToastRecord> list = mToastQueue;
4346 int len = list.size();
4347 for (int i=0; i<len; i++) {
4348 ToastRecord r = list.get(i);
4349 if (r.pkg.equals(pkg)) {
4356 @GuardedBy("mToastQueue")
4357 void keepProcessAliveIfNeededLocked(int pid)
4359 int toastCount = 0; // toasts from this pid
4360 ArrayList<ToastRecord> list = mToastQueue;
4361 int N = list.size();
4362 for (int i=0; i<N; i++) {
4363 ToastRecord r = list.get(i);
4369 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
4370 } catch (RemoteException e) {
4371 // Shouldn't happen.
4375 private void handleRankingReconsideration(Message message) {
4376 if (!(message.obj instanceof RankingReconsideration)) return;
4377 RankingReconsideration recon = (RankingReconsideration) message.obj;
4380 synchronized (mNotificationLock) {
4381 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4382 if (record == null) {
4385 int indexBefore = findNotificationRecordIndexLocked(record);
4386 boolean interceptBefore = record.isIntercepted();
4387 float contactAffinityBefore = record.getContactAffinity();
4388 int visibilityBefore = record.getPackageVisibilityOverride();
4389 recon.applyChangesLocked(record);
4390 applyZenModeLocked(record);
4391 mRankingHelper.sort(mNotificationList);
4392 int indexAfter = findNotificationRecordIndexLocked(record);
4393 boolean interceptAfter = record.isIntercepted();
4394 float contactAffinityAfter = record.getContactAffinity();
4395 int visibilityAfter = record.getPackageVisibilityOverride();
4396 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4397 || visibilityBefore != visibilityAfter;
4398 if (interceptBefore && !interceptAfter
4399 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
4400 buzzBeepBlinkLocked(record);
4404 mHandler.scheduleSendRankingUpdate();
4408 void handleRankingSort() {
4409 if (mRankingHelper == null) return;
4410 synchronized (mNotificationLock) {
4411 final int N = mNotificationList.size();
4412 // Any field that can change via one of the extractors needs to be added here.
4413 ArrayList<String> orderBefore = new ArrayList<>(N);
4414 int[] visibilities = new int[N];
4415 boolean[] showBadges = new boolean[N];
4416 ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
4417 ArrayList<String> groupKeyBefore = new ArrayList<>(N);
4418 ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
4419 ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
4420 for (int i = 0; i < N; i++) {
4421 final NotificationRecord r = mNotificationList.get(i);
4422 orderBefore.add(r.getKey());
4423 visibilities[i] = r.getPackageVisibilityOverride();
4424 showBadges[i] = r.canShowBadge();
4425 channelBefore.add(r.getChannel());
4426 groupKeyBefore.add(r.getGroupKey());
4427 overridePeopleBefore.add(r.getPeopleOverride());
4428 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
4429 mRankingHelper.extractSignals(r);
4431 mRankingHelper.sort(mNotificationList);
4432 for (int i = 0; i < N; i++) {
4433 final NotificationRecord r = mNotificationList.get(i);
4434 if (!orderBefore.get(i).equals(r.getKey())
4435 || visibilities[i] != r.getPackageVisibilityOverride()
4436 || showBadges[i] != r.canShowBadge()
4437 || !Objects.equals(channelBefore.get(i), r.getChannel())
4438 || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
4439 || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
4440 || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
4441 mHandler.scheduleSendRankingUpdate();
4448 @GuardedBy("mNotificationLock")
4449 private void recordCallerLocked(NotificationRecord record) {
4450 if (mZenModeHelper.isCall(record)) {
4451 mZenModeHelper.recordCaller(record);
4455 // let zen mode evaluate this record
4456 @GuardedBy("mNotificationLock")
4457 private void applyZenModeLocked(NotificationRecord record) {
4458 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4459 if (record.isIntercepted()) {
4460 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4461 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4462 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4463 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4464 record.setSuppressedVisualEffects(suppressed);
4466 record.setSuppressedVisualEffects(0);
4470 @GuardedBy("mNotificationLock")
4471 private int findNotificationRecordIndexLocked(NotificationRecord target) {
4472 return mRankingHelper.indexOf(mNotificationList, target);
4475 private void handleSendRankingUpdate() {
4476 synchronized (mNotificationLock) {
4477 mListeners.notifyRankingUpdateLocked();
4481 private void scheduleListenerHintsChanged(int state) {
4482 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4483 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4486 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4487 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4488 mHandler.obtainMessage(
4489 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4490 listenerInterruptionFilter,
4494 private void handleListenerHintsChanged(int hints) {
4495 synchronized (mNotificationLock) {
4496 mListeners.notifyListenerHintsChangedLocked(hints);
4500 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4501 synchronized (mNotificationLock) {
4502 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4506 protected class WorkerHandler extends Handler
4508 public WorkerHandler(Looper looper) {
4513 public void handleMessage(Message msg)
4517 case MESSAGE_TIMEOUT:
4518 handleTimeout((ToastRecord)msg.obj);
4520 case MESSAGE_SAVE_POLICY_FILE:
4521 handleSavePolicyFile();
4523 case MESSAGE_SEND_RANKING_UPDATE:
4524 handleSendRankingUpdate();
4526 case MESSAGE_LISTENER_HINTS_CHANGED:
4527 handleListenerHintsChanged(msg.arg1);
4529 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4530 handleListenerInterruptionFilterChanged(msg.arg1);
4535 protected void scheduleSendRankingUpdate() {
4536 if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4537 Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
4544 private final class RankingHandlerWorker extends Handler implements RankingHandler
4546 public RankingHandlerWorker(Looper looper) {
4551 public void handleMessage(Message msg) {
4553 case MESSAGE_RECONSIDER_RANKING:
4554 handleRankingReconsideration(msg);
4556 case MESSAGE_RANKING_SORT:
4557 handleRankingSort();
4562 public void requestSort() {
4563 removeMessages(MESSAGE_RANKING_SORT);
4564 Message msg = Message.obtain();
4565 msg.what = MESSAGE_RANKING_SORT;
4569 public void requestReconsideration(RankingReconsideration recon) {
4570 Message m = Message.obtain(this,
4571 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4572 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4573 sendMessageDelayed(m, delay);
4578 // ============================================================================
4579 static int clamp(int x, int low, int high) {
4580 return (x < low) ? low : ((x > high) ? high : x);
4583 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4584 if (!mAccessibilityManager.isEnabled()) {
4588 AccessibilityEvent event =
4589 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4590 event.setPackageName(packageName);
4591 event.setClassName(Notification.class.getName());
4592 event.setParcelableData(notification);
4593 CharSequence tickerText = notification.tickerText;
4594 if (!TextUtils.isEmpty(tickerText)) {
4595 event.getText().add(tickerText);
4598 mAccessibilityManager.sendAccessibilityEvent(event);
4602 * Removes all NotificationsRecords with the same key as the given notification record
4603 * from both lists. Do not call this method while iterating over either list.
4605 @GuardedBy("mNotificationLock")
4606 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4607 // Remove from both lists, either list could have a separate Record for what is
4608 // effectively the same notification.
4609 boolean wasPosted = false;
4610 NotificationRecord recordInList = null;
4611 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4613 mNotificationList.remove(recordInList);
4614 mNotificationsByKey.remove(recordInList.sbn.getKey());
4617 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4619 mEnqueuedNotifications.remove(recordInList);
4624 @GuardedBy("mNotificationLock")
4625 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4626 boolean wasPosted, String listenerName) {
4627 final String canceledKey = r.getKey();
4630 recordCallerLocked(r);
4634 if (r.getNotification().deleteIntent != null) {
4636 r.getNotification().deleteIntent.send();
4637 } catch (PendingIntent.CanceledException ex) {
4638 // do nothing - there's no relevant way to recover, and
4639 // no reason to let this propagate
4640 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4645 // Only cancel these if this notification actually got to be posted.
4648 if (r.getNotification().getSmallIcon() != null) {
4649 if (reason != REASON_SNOOZED) {
4650 r.isCanceled = true;
4652 mListeners.notifyRemovedLocked(r.sbn, reason);
4653 mHandler.post(new Runnable() {
4656 mGroupHelper.onNotificationRemoved(r.sbn);
4662 if (canceledKey.equals(mSoundNotificationKey)) {
4663 mSoundNotificationKey = null;
4664 final long identity = Binder.clearCallingIdentity();
4666 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4667 if (player != null) {
4670 } catch (RemoteException e) {
4672 Binder.restoreCallingIdentity(identity);
4677 if (canceledKey.equals(mVibrateNotificationKey)) {
4678 mVibrateNotificationKey = null;
4679 long identity = Binder.clearCallingIdentity();
4684 Binder.restoreCallingIdentity(identity);
4689 mLights.remove(canceledKey);
4692 // Record usage stats
4693 // TODO: add unbundling stats?
4696 case REASON_CANCEL_ALL:
4697 case REASON_LISTENER_CANCEL:
4698 case REASON_LISTENER_CANCEL_ALL:
4699 mUsageStats.registerDismissedByUser(r);
4701 case REASON_APP_CANCEL:
4702 case REASON_APP_CANCEL_ALL:
4703 mUsageStats.registerRemovedByApp(r);
4707 String groupKey = r.getGroupKey();
4708 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4709 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4710 mSummaryByGroupKey.remove(groupKey);
4712 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4713 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4714 summaries.remove(r.sbn.getPackageName());
4717 // Save it for users of getHistoricalNotifications()
4718 mArchive.record(r.sbn);
4720 final long now = System.currentTimeMillis();
4721 MetricsLogger.action(r.getLogMaker(now)
4722 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4723 .setType(MetricsEvent.TYPE_DISMISS)
4724 .setSubtype(reason));
4725 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4726 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
4730 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4731 * and none of the {@code mustNotHaveFlags}.
4733 void cancelNotification(final int callingUid, final int callingPid,
4734 final String pkg, final String tag, final int id,
4735 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4736 final int userId, final int reason, final ManagedServiceInfo listener) {
4737 // In enqueueNotificationInternal notifications are added by scheduling the
4738 // work on the worker handler. Hence, we also schedule the cancel on this
4739 // handler to avoid a scenario where an add notification call followed by a
4740 // remove notification call ends up in not removing the notification.
4741 mHandler.post(new Runnable() {
4744 String listenerName = listener == null ? null : listener.component.toShortString();
4745 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4746 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4748 synchronized (mNotificationLock) {
4749 // If the notification is currently enqueued, repost this runnable so it has a
4750 // chance to notify listeners
4751 if ((findNotificationByListLocked(
4752 mEnqueuedNotifications, pkg, tag, id, userId)) != null) {
4753 mHandler.post(this);
4755 // Look for the notification in the posted list, since we already checked enq
4756 NotificationRecord r = findNotificationByListLocked(
4757 mNotificationList, pkg, tag, id, userId);
4759 // The notification was found, check if it should be removed.
4761 // Ideally we'd do this in the caller of this method. However, that would
4762 // require the caller to also find the notification.
4763 if (reason == REASON_CLICK) {
4764 mUsageStats.registerClickedByUser(r);
4767 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4770 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4774 // Cancel the notification.
4775 boolean wasPosted = removeFromNotificationListsLocked(r);
4776 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4777 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4779 updateLightsLocked();
4781 // No notification was found, assume that it is snoozed and cancel it.
4782 if (reason != REASON_SNOOZED) {
4783 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4795 * Determine whether the userId applies to the notification in question, either because
4796 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4798 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4800 // looking for USER_ALL notifications? match everything
4801 userId == UserHandle.USER_ALL
4802 // a notification sent to USER_ALL matches any query
4803 || r.getUserId() == UserHandle.USER_ALL
4804 // an exact user match
4805 || r.getUserId() == userId;
4809 * Determine whether the userId applies to the notification in question, either because
4810 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4811 * because it matches one of the users profiles.
4813 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4814 return notificationMatchesUserId(r, userId)
4815 || mUserProfiles.isCurrentProfile(r.getUserId());
4819 * Cancels all notifications from a given package that have all of the
4820 * {@code mustHaveFlags}.
4822 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4823 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4824 ManagedServiceInfo listener) {
4825 mHandler.post(new Runnable() {
4828 String listenerName = listener == null ? null : listener.component.toShortString();
4829 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4830 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4833 // Why does this parameter exist? Do we actually want to execute the above if doit
4839 synchronized (mNotificationLock) {
4840 FlagChecker flagChecker = (int flags) -> {
4841 if ((flags & mustHaveFlags) != mustHaveFlags) {
4844 if ((flags & mustNotHaveFlags) != 0) {
4849 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4850 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4851 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4852 listenerName, true /* wasPosted */);
4853 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4854 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4855 flagChecker, false /*includeCurrentProfiles*/, userId,
4856 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4857 mSnoozeHelper.cancel(userId, pkg);
4863 private interface FlagChecker {
4864 // Returns false if these flags do not pass the defined flag test.
4865 public boolean apply(int flags);
4868 @GuardedBy("mNotificationLock")
4869 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4870 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4871 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4872 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4873 ArrayList<NotificationRecord> canceledNotifications = null;
4874 for (int i = notificationList.size() - 1; i >= 0; --i) {
4875 NotificationRecord r = notificationList.get(i);
4876 if (includeCurrentProfiles) {
4877 if (!notificationMatchesCurrentProfiles(r, userId)) {
4880 } else if (!notificationMatchesUserId(r, userId)) {
4883 // Don't remove notifications to all, if there's no package name specified
4884 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4887 if (!flagChecker.apply(r.getFlags())) {
4890 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4893 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4896 if (canceledNotifications == null) {
4897 canceledNotifications = new ArrayList<>();
4899 notificationList.remove(i);
4900 mNotificationsByKey.remove(r.getKey());
4901 canceledNotifications.add(r);
4902 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4904 if (canceledNotifications != null) {
4905 final int M = canceledNotifications.size();
4906 for (int i = 0; i < M; i++) {
4907 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4908 listenerName, false /* sendDelete */, flagChecker);
4910 updateLightsLocked();
4914 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4915 ManagedServiceInfo listener) {
4916 String listenerName = listener == null ? null : listener.component.toShortString();
4917 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4922 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4923 snoozeCriterionId, listenerName));
4925 // Needs to post so that it can cancel notifications not yet enqueued.
4926 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4929 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4930 String listenerName = listener == null ? null : listener.component.toShortString();
4932 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4934 mSnoozeHelper.repost(key);
4938 @GuardedBy("mNotificationLock")
4939 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4940 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4941 mHandler.post(new Runnable() {
4944 synchronized (mNotificationLock) {
4945 String listenerName =
4946 listener == null ? null : listener.component.toShortString();
4947 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4948 null, userId, 0, 0, reason, listenerName);
4950 FlagChecker flagChecker = (int flags) -> {
4951 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4958 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4959 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4960 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4961 listenerName, true);
4962 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4963 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4964 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4965 reason, listenerName, false);
4966 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4972 // Warning: The caller is responsible for invoking updateLightsLocked().
4973 @GuardedBy("mNotificationLock")
4974 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4975 String listenerName, boolean sendDelete, FlagChecker flagChecker) {
4976 Notification n = r.getNotification();
4977 if (!n.isGroupSummary()) {
4981 String pkg = r.sbn.getPackageName();
4984 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4988 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4989 sendDelete, true, flagChecker);
4990 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4991 listenerName, sendDelete, false, flagChecker);
4994 @GuardedBy("mNotificationLock")
4995 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4996 NotificationRecord parentNotification, int callingUid, int callingPid,
4997 String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
4998 final String pkg = parentNotification.sbn.getPackageName();
4999 final int userId = parentNotification.getUserId();
5000 final int reason = REASON_GROUP_SUMMARY_CANCELED;
5001 for (int i = notificationList.size() - 1; i >= 0; i--) {
5002 final NotificationRecord childR = notificationList.get(i);
5003 final StatusBarNotification childSbn = childR.sbn;
5004 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
5005 childR.getGroupKey().equals(parentNotification.getGroupKey())
5006 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
5007 && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
5008 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
5009 childSbn.getTag(), userId, 0, 0, reason, listenerName);
5010 notificationList.remove(i);
5011 mNotificationsByKey.remove(childR.getKey());
5012 cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName);
5017 @GuardedBy("mNotificationLock")
5018 void updateLightsLocked()
5020 // handle notification lights
5021 NotificationRecord ledNotification = null;
5022 while (ledNotification == null && !mLights.isEmpty()) {
5023 final String owner = mLights.get(mLights.size() - 1);
5024 ledNotification = mNotificationsByKey.get(owner);
5025 if (ledNotification == null) {
5026 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
5027 mLights.remove(owner);
5031 // Don't flash while we are in a call or screen is on
5032 if (ledNotification == null || mInCall || mScreenOn) {
5033 mNotificationLight.turnOff();
5035 NotificationRecord.Light light = ledNotification.getLight();
5036 if (light != null && mNotificationPulseEnabled) {
5038 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
5039 light.onMs, light.offMs);
5044 @GuardedBy("mNotificationLock")
5045 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
5046 String groupKey, int userId) {
5047 List<NotificationRecord> records = new ArrayList<>();
5048 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
5050 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
5055 @GuardedBy("mNotificationLock")
5056 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
5057 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
5058 List<NotificationRecord> records = new ArrayList<>();
5059 final int len = list.size();
5060 for (int i = 0; i < len; i++) {
5061 NotificationRecord r = list.get(i);
5062 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
5063 && r.sbn.getPackageName().equals(pkg)) {
5070 // Searches both enqueued and posted notifications by key.
5071 // TODO: need to combine a bunch of these getters with slightly different behavior.
5072 // TODO: Should enqueuing just add to mNotificationsByKey instead?
5073 @GuardedBy("mNotificationLock")
5074 private NotificationRecord findNotificationByKeyLocked(String key) {
5075 NotificationRecord r;
5076 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
5079 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
5085 @GuardedBy("mNotificationLock")
5086 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
5087 NotificationRecord r;
5088 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
5091 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
5098 @GuardedBy("mNotificationLock")
5099 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5100 String pkg, String tag, int id, int userId) {
5101 final int len = list.size();
5102 for (int i = 0; i < len; i++) {
5103 NotificationRecord r = list.get(i);
5104 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
5105 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
5112 @GuardedBy("mNotificationLock")
5113 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5115 final int N = list.size();
5116 for (int i = 0; i < N; i++) {
5117 if (key.equals(list.get(i).getKey())) {
5124 @GuardedBy("mNotificationLock")
5125 int indexOfNotificationLocked(String key) {
5126 final int N = mNotificationList.size();
5127 for (int i = 0; i < N; i++) {
5128 if (key.equals(mNotificationList.get(i).getKey())) {
5135 private void updateNotificationPulse() {
5136 synchronized (mNotificationLock) {
5137 updateLightsLocked();
5141 protected boolean isCallingUidSystem() {
5142 final int uid = Binder.getCallingUid();
5143 return uid == Process.SYSTEM_UID;
5146 protected boolean isUidSystemOrPhone(int uid) {
5147 final int appid = UserHandle.getAppId(uid);
5148 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
5151 // TODO: Most calls should probably move to isCallerSystem.
5152 protected boolean isCallerSystemOrPhone() {
5153 return isUidSystemOrPhone(Binder.getCallingUid());
5156 private void checkCallerIsSystemOrShell() {
5157 if (Binder.getCallingUid() == Process.SHELL_UID) {
5160 checkCallerIsSystem();
5163 private void checkCallerIsSystem() {
5164 if (isCallerSystemOrPhone()) {
5167 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
5170 private void checkCallerIsSystemOrSameApp(String pkg) {
5171 if (isCallerSystemOrPhone()) {
5174 checkCallerIsSameApp(pkg);
5177 private boolean isCallerInstantApp(String pkg) {
5178 // System is always allowed to act for ephemeral apps.
5179 if (isCallerSystemOrPhone()) {
5183 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
5186 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
5187 UserHandle.getCallingUserId());
5189 throw new SecurityException("Unknown package " + pkg);
5191 return ai.isInstantApp();
5192 } catch (RemoteException re) {
5193 throw new SecurityException("Unknown package " + pkg, re);
5198 private void checkCallerIsSameApp(String pkg) {
5199 final int uid = Binder.getCallingUid();
5201 ApplicationInfo ai = mPackageManager.getApplicationInfo(
5202 pkg, 0, UserHandle.getCallingUserId());
5204 throw new SecurityException("Unknown package " + pkg);
5206 if (!UserHandle.isSameApp(ai.uid, uid)) {
5207 throw new SecurityException("Calling uid " + uid + " gave package "
5208 + pkg + " which is owned by uid " + ai.uid);
5210 } catch (RemoteException re) {
5211 throw new SecurityException("Unknown package " + pkg + "\n" + re);
5215 private static String callStateToString(int state) {
5217 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
5218 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
5219 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
5220 default: return "CALL_STATE_UNKNOWN_" + state;
5224 private void listenForCallState() {
5225 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
5227 public void onCallStateChanged(int state, String incomingNumber) {
5228 if (mCallState == state) return;
5229 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
5232 }, PhoneStateListener.LISTEN_CALL_STATE);
5236 * Generates a NotificationRankingUpdate from 'sbns', considering only
5237 * notifications visible to the given listener.
5239 @GuardedBy("mNotificationLock")
5240 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
5241 final int N = mNotificationList.size();
5242 ArrayList<String> keys = new ArrayList<String>(N);
5243 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
5244 ArrayList<Integer> importance = new ArrayList<>(N);
5245 Bundle overrideGroupKeys = new Bundle();
5246 Bundle visibilityOverrides = new Bundle();
5247 Bundle suppressedVisualEffects = new Bundle();
5248 Bundle explanation = new Bundle();
5249 Bundle channels = new Bundle();
5250 Bundle overridePeople = new Bundle();
5251 Bundle snoozeCriteria = new Bundle();
5252 Bundle showBadge = new Bundle();
5253 for (int i = 0; i < N; i++) {
5254 NotificationRecord record = mNotificationList.get(i);
5255 if (!isVisibleToListener(record.sbn, info)) {
5258 final String key = record.sbn.getKey();
5260 importance.add(record.getImportance());
5261 if (record.getImportanceExplanation() != null) {
5262 explanation.putCharSequence(key, record.getImportanceExplanation());
5264 if (record.isIntercepted()) {
5265 interceptedKeys.add(key);
5268 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
5269 if (record.getPackageVisibilityOverride()
5270 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
5271 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
5273 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
5274 channels.putParcelable(key, record.getChannel());
5275 overridePeople.putStringArrayList(key, record.getPeopleOverride());
5276 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
5277 showBadge.putBoolean(key, record.canShowBadge());
5279 final int M = keys.size();
5280 String[] keysAr = keys.toArray(new String[M]);
5281 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
5282 int[] importanceAr = new int[M];
5283 for (int i = 0; i < M; i++) {
5284 importanceAr[i] = importance.get(i);
5286 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
5287 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
5288 channels, overridePeople, snoozeCriteria, showBadge);
5291 boolean hasCompanionDevice(ManagedServiceInfo info) {
5292 if (mCompanionManager == null) {
5293 mCompanionManager = getCompanionManager();
5295 // Companion mgr doesn't exist on all device types
5296 if (mCompanionManager == null) {
5299 long identity = Binder.clearCallingIdentity();
5301 List<String> associations = mCompanionManager.getAssociations(
5302 info.component.getPackageName(), info.userid);
5303 if (!ArrayUtils.isEmpty(associations)) {
5306 } catch (SecurityException se) {
5307 // Not a privileged listener
5308 } catch (RemoteException re) {
5309 Slog.e(TAG, "Cannot reach companion device service", re);
5310 } catch (Exception e) {
5311 Slog.e(TAG, "Cannot verify listener " + info, e);
5313 Binder.restoreCallingIdentity(identity);
5318 protected ICompanionDeviceManager getCompanionManager() {
5319 return ICompanionDeviceManager.Stub.asInterface(
5320 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
5323 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5324 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5327 // TODO: remove this for older listeners.
5331 private boolean isPackageSuspendedForUser(String pkg, int uid) {
5332 int userId = UserHandle.getUserId(uid);
5334 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
5335 } catch (RemoteException re) {
5336 throw new SecurityException("Could not talk to package manager service");
5337 } catch (IllegalArgumentException ex) {
5338 // Package not found.
5343 private class TrimCache {
5344 StatusBarNotification heavy;
5345 StatusBarNotification sbnClone;
5346 StatusBarNotification sbnCloneLight;
5348 TrimCache(StatusBarNotification sbn) {
5352 StatusBarNotification ForListener(ManagedServiceInfo info) {
5353 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5354 if (sbnCloneLight == null) {
5355 sbnCloneLight = heavy.cloneLight();
5357 return sbnCloneLight;
5359 if (sbnClone == null) {
5360 sbnClone = heavy.clone();
5367 public class NotificationAssistants extends ManagedServices {
5368 static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
5370 public NotificationAssistants(IPackageManager pm) {
5371 super(getContext(), mNotificationLock, mUserProfiles, pm);
5375 protected Config getConfig() {
5376 Config c = new Config();
5377 c.caption = "notification assistant service";
5378 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5379 c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
5380 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5381 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5382 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5383 c.clientLabel = R.string.notification_ranker_binding_label;
5388 protected IInterface asInterface(IBinder binder) {
5389 return INotificationListener.Stub.asInterface(binder);
5393 protected boolean checkType(IInterface service) {
5394 return service instanceof INotificationListener;
5398 protected void onServiceAdded(ManagedServiceInfo info) {
5399 mListeners.registerGuestService(info);
5403 @GuardedBy("mNotificationLock")
5404 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5405 mListeners.unregisterService(removed.service, removed.userid);
5408 public void onNotificationEnqueued(final NotificationRecord r) {
5409 final StatusBarNotification sbn = r.sbn;
5410 TrimCache trimCache = new TrimCache(sbn);
5412 // There should be only one, but it's a list, so while we enforce
5413 // singularity elsewhere, we keep it general here, to avoid surprises.
5414 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5415 boolean sbnVisible = isVisibleToListener(sbn, info);
5420 final int importance = r.getImportance();
5421 final boolean fromUser = r.isImportanceFromUser();
5422 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5423 mHandler.post(new Runnable() {
5426 notifyEnqueued(info, sbnToPost);
5432 private void notifyEnqueued(final ManagedServiceInfo info,
5433 final StatusBarNotification sbn) {
5434 final INotificationListener assistant = (INotificationListener) info.service;
5435 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5437 assistant.onNotificationEnqueued(sbnHolder);
5438 } catch (RemoteException ex) {
5439 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5444 * asynchronously notify the assistant that a notification has been snoozed until a
5447 @GuardedBy("mNotificationLock")
5448 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5449 final String snoozeCriterionId) {
5450 TrimCache trimCache = new TrimCache(sbn);
5451 for (final ManagedServiceInfo info : getServices()) {
5452 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5453 mHandler.post(new Runnable() {
5456 final INotificationListener assistant =
5457 (INotificationListener) info.service;
5458 StatusBarNotificationHolder sbnHolder
5459 = new StatusBarNotificationHolder(sbnToPost);
5461 assistant.onNotificationSnoozedUntilContext(
5462 sbnHolder, snoozeCriterionId);
5463 } catch (RemoteException ex) {
5464 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5471 public boolean isEnabled() {
5472 return !getServices().isEmpty();
5476 public class NotificationListeners extends ManagedServices {
5477 static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
5479 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5481 public NotificationListeners(IPackageManager pm) {
5482 super(getContext(), mNotificationLock, mUserProfiles, pm);
5487 protected Config getConfig() {
5488 Config c = new Config();
5489 c.caption = "notification listener";
5490 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5491 c.xmlTag = TAG_ENABLED_NOTIFICATION_LISTENERS;
5492 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5493 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5494 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5495 c.clientLabel = R.string.notification_listener_binding_label;
5500 protected IInterface asInterface(IBinder binder) {
5501 return INotificationListener.Stub.asInterface(binder);
5505 protected boolean checkType(IInterface service) {
5506 return service instanceof INotificationListener;
5510 public void onServiceAdded(ManagedServiceInfo info) {
5511 final INotificationListener listener = (INotificationListener) info.service;
5512 final NotificationRankingUpdate update;
5513 synchronized (mNotificationLock) {
5514 update = makeRankingUpdateLocked(info);
5517 listener.onListenerConnected(update);
5518 } catch (RemoteException e) {
5524 @GuardedBy("mNotificationLock")
5525 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5526 if (removeDisabledHints(removed)) {
5527 updateListenerHintsLocked();
5528 updateEffectsSuppressorLocked();
5530 mLightTrimListeners.remove(removed);
5533 @GuardedBy("mNotificationLock")
5534 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5535 if (trim == TRIM_LIGHT) {
5536 mLightTrimListeners.add(info);
5538 mLightTrimListeners.remove(info);
5542 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5543 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5547 * asynchronously notify all listeners about a new notification
5550 * Also takes care of removing a notification that has been visible to a listener before,
5551 * but isn't anymore.
5553 @GuardedBy("mNotificationLock")
5554 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5555 // Lazily initialized snapshots of the notification.
5556 TrimCache trimCache = new TrimCache(sbn);
5558 for (final ManagedServiceInfo info : getServices()) {
5559 boolean sbnVisible = isVisibleToListener(sbn, info);
5560 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5561 // This notification hasn't been and still isn't visible -> ignore.
5562 if (!oldSbnVisible && !sbnVisible) {
5565 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5567 // This notification became invisible -> remove the old one.
5568 if (oldSbnVisible && !sbnVisible) {
5569 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5570 mHandler.post(new Runnable() {
5573 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5579 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5580 mHandler.post(new Runnable() {
5583 notifyPosted(info, sbnToPost, update);
5590 * asynchronously notify all listeners about a removed notification
5592 @GuardedBy("mNotificationLock")
5593 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5594 // make a copy in case changes are made to the underlying Notification object
5595 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5597 final StatusBarNotification sbnLight = sbn.cloneLight();
5598 for (final ManagedServiceInfo info : getServices()) {
5599 if (!isVisibleToListener(sbn, info)) {
5602 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5603 mHandler.post(new Runnable() {
5606 notifyRemoved(info, sbnLight, update, reason);
5613 * asynchronously notify all listeners about a reordering of notifications
5615 @GuardedBy("mNotificationLock")
5616 public void notifyRankingUpdateLocked() {
5617 for (final ManagedServiceInfo serviceInfo : getServices()) {
5618 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5621 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5622 mHandler.post(new Runnable() {
5625 notifyRankingUpdate(serviceInfo, update);
5631 @GuardedBy("mNotificationLock")
5632 public void notifyListenerHintsChangedLocked(final int hints) {
5633 for (final ManagedServiceInfo serviceInfo : getServices()) {
5634 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5637 mHandler.post(new Runnable() {
5640 notifyListenerHintsChanged(serviceInfo, hints);
5646 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5647 for (final ManagedServiceInfo serviceInfo : getServices()) {
5648 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5651 mHandler.post(new Runnable() {
5654 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5660 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5661 final NotificationChannel channel, final int modificationType) {
5662 if (channel == null) {
5665 for (final ManagedServiceInfo serviceInfo : getServices()) {
5666 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5670 BackgroundThread.getHandler().post(() -> {
5671 if (hasCompanionDevice(serviceInfo)) {
5672 notifyNotificationChannelChanged(
5673 serviceInfo, pkg, user, channel, modificationType);
5679 protected void notifyNotificationChannelGroupChanged(
5680 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5681 final int modificationType) {
5682 if (group == null) {
5685 for (final ManagedServiceInfo serviceInfo : getServices()) {
5686 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5690 BackgroundThread.getHandler().post(() -> {
5691 if (hasCompanionDevice(serviceInfo)) {
5692 notifyNotificationChannelGroupChanged(
5693 serviceInfo, pkg, user, group, modificationType);
5699 private void notifyPosted(final ManagedServiceInfo info,
5700 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5701 final INotificationListener listener = (INotificationListener) info.service;
5702 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5704 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5705 } catch (RemoteException ex) {
5706 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5710 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5711 NotificationRankingUpdate rankingUpdate, int reason) {
5712 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5715 final INotificationListener listener = (INotificationListener) info.service;
5716 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5718 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5719 } catch (RemoteException ex) {
5720 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5724 private void notifyRankingUpdate(ManagedServiceInfo info,
5725 NotificationRankingUpdate rankingUpdate) {
5726 final INotificationListener listener = (INotificationListener) info.service;
5728 listener.onNotificationRankingUpdate(rankingUpdate);
5729 } catch (RemoteException ex) {
5730 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5734 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5735 final INotificationListener listener = (INotificationListener) info.service;
5737 listener.onListenerHintsChanged(hints);
5738 } catch (RemoteException ex) {
5739 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5743 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5744 int interruptionFilter) {
5745 final INotificationListener listener = (INotificationListener) info.service;
5747 listener.onInterruptionFilterChanged(interruptionFilter);
5748 } catch (RemoteException ex) {
5749 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5753 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5754 final String pkg, final UserHandle user, final NotificationChannel channel,
5755 final int modificationType) {
5756 final INotificationListener listener = (INotificationListener) info.service;
5758 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5759 } catch (RemoteException ex) {
5760 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5764 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5765 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5766 final int modificationType) {
5767 final INotificationListener listener = (INotificationListener) info.service;
5769 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5770 } catch (RemoteException ex) {
5771 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5775 public boolean isListenerPackage(String packageName) {
5776 if (packageName == null) {
5779 // TODO: clean up locking object later
5780 synchronized (mNotificationLock) {
5781 for (final ManagedServiceInfo serviceInfo : getServices()) {
5782 if (packageName.equals(serviceInfo.component.getPackageName())) {
5791 public static final class DumpFilter {
5792 public boolean filtered = false;
5793 public String pkgFilter;
5796 public boolean stats;
5797 public boolean redact = true;
5798 public boolean proto = false;
5800 public static DumpFilter parseFromArguments(String[] args) {
5801 final DumpFilter filter = new DumpFilter();
5802 for (int ai = 0; ai < args.length; ai++) {
5803 final String a = args[ai];
5804 if ("--proto".equals(args[0])) {
5805 filter.proto = true;
5807 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5808 filter.redact = false;
5809 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5810 if (ai < args.length-1) {
5812 filter.pkgFilter = args[ai].trim().toLowerCase();
5813 if (filter.pkgFilter.isEmpty()) {
5814 filter.pkgFilter = null;
5816 filter.filtered = true;
5819 } else if ("--zen".equals(a) || "zen".equals(a)) {
5820 filter.filtered = true;
5822 } else if ("--stats".equals(a)) {
5823 filter.stats = true;
5824 if (ai < args.length-1) {
5826 filter.since = Long.parseLong(args[ai]);
5835 public boolean matches(StatusBarNotification sbn) {
5836 if (!filtered) return true;
5837 return zen ? true : sbn != null
5838 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5841 public boolean matches(ComponentName component) {
5842 if (!filtered) return true;
5843 return zen ? true : component != null && matches(component.getPackageName());
5846 public boolean matches(String pkg) {
5847 if (!filtered) return true;
5848 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5852 public String toString() {
5853 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5858 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5859 * binder without sending large amounts of data over a oneway transaction.
5861 private static final class StatusBarNotificationHolder
5862 extends IStatusBarNotificationHolder.Stub {
5863 private StatusBarNotification mValue;
5865 public StatusBarNotificationHolder(StatusBarNotification value) {
5869 /** Get the held value and clear it. This function should only be called once per holder */
5871 public StatusBarNotification get() {
5872 StatusBarNotification value = mValue;
5878 private class ShellCmd extends ShellCommand {
5879 public static final String USAGE = "help\n"
5880 + "allow_listener COMPONENT [user_id]\n"
5881 + "disallow_listener COMPONENT [user_id]\n"
5882 + "set_assistant COMPONENT\n"
5883 + "remove_assistant COMPONENT\n"
5884 + "allow_dnd PACKAGE\n"
5885 + "disallow_dnd PACKAGE";
5888 public int onCommand(String cmd) {
5890 return handleDefaultCommands(cmd);
5892 final PrintWriter pw = getOutPrintWriter();
5896 getBinderService().setNotificationPolicyAccessGranted(
5897 getNextArgRequired(), true);
5901 case "disallow_dnd": {
5902 getBinderService().setNotificationPolicyAccessGranted(
5903 getNextArgRequired(), false);
5906 case "allow_listener": {
5907 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5909 pw.println("Invalid listener - must be a ComponentName");
5912 String userId = getNextArg();
5913 if (userId == null) {
5914 getBinderService().setNotificationListenerAccessGranted(cn, true);
5916 getBinderService().setNotificationListenerAccessGrantedForUser(
5917 cn, Integer.parseInt(userId), true);
5921 case "disallow_listener": {
5922 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5924 pw.println("Invalid listener - must be a ComponentName");
5927 String userId = getNextArg();
5928 if (userId == null) {
5929 getBinderService().setNotificationListenerAccessGranted(cn, false);
5931 getBinderService().setNotificationListenerAccessGrantedForUser(
5932 cn, Integer.parseInt(userId), false);
5936 case "allow_assistant": {
5937 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5939 pw.println("Invalid assistant - must be a ComponentName");
5942 getBinderService().setNotificationAssistantAccessGranted(cn, true);
5945 case "disallow_assistant": {
5946 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5948 pw.println("Invalid assistant - must be a ComponentName");
5951 getBinderService().setNotificationAssistantAccessGranted(cn, false);
5956 return handleDefaultCommands(cmd);
5958 } catch (Exception e) {
5959 pw.println("Error occurred. Check logcat for details. " + e.getMessage());
5960 Slog.e(TAG, "Error running shell command", e);
5966 public void onHelp() {
5967 getOutPrintWriter().println(USAGE);