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 // Look for the notification, searching both the posted and enqueued lists.
4750 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4752 // The notification was found, check if it should be removed.
4754 // Ideally we'd do this in the caller of this method. However, that would
4755 // require the caller to also find the notification.
4756 if (reason == REASON_CLICK) {
4757 mUsageStats.registerClickedByUser(r);
4760 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4763 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4767 // Cancel the notification.
4768 boolean wasPosted = removeFromNotificationListsLocked(r);
4769 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4770 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4772 updateLightsLocked();
4774 // No notification was found, assume that it is snoozed and cancel it.
4775 if (reason != REASON_SNOOZED) {
4776 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4788 * Determine whether the userId applies to the notification in question, either because
4789 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4791 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4793 // looking for USER_ALL notifications? match everything
4794 userId == UserHandle.USER_ALL
4795 // a notification sent to USER_ALL matches any query
4796 || r.getUserId() == UserHandle.USER_ALL
4797 // an exact user match
4798 || r.getUserId() == userId;
4802 * Determine whether the userId applies to the notification in question, either because
4803 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4804 * because it matches one of the users profiles.
4806 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4807 return notificationMatchesUserId(r, userId)
4808 || mUserProfiles.isCurrentProfile(r.getUserId());
4812 * Cancels all notifications from a given package that have all of the
4813 * {@code mustHaveFlags}.
4815 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4816 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4817 ManagedServiceInfo listener) {
4818 mHandler.post(new Runnable() {
4821 String listenerName = listener == null ? null : listener.component.toShortString();
4822 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4823 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4826 // Why does this parameter exist? Do we actually want to execute the above if doit
4832 synchronized (mNotificationLock) {
4833 FlagChecker flagChecker = (int flags) -> {
4834 if ((flags & mustHaveFlags) != mustHaveFlags) {
4837 if ((flags & mustNotHaveFlags) != 0) {
4842 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4843 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4844 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4845 listenerName, true /* wasPosted */);
4846 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4847 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4848 flagChecker, false /*includeCurrentProfiles*/, userId,
4849 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4850 mSnoozeHelper.cancel(userId, pkg);
4856 private interface FlagChecker {
4857 // Returns false if these flags do not pass the defined flag test.
4858 public boolean apply(int flags);
4861 @GuardedBy("mNotificationLock")
4862 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4863 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4864 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4865 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4866 ArrayList<NotificationRecord> canceledNotifications = null;
4867 for (int i = notificationList.size() - 1; i >= 0; --i) {
4868 NotificationRecord r = notificationList.get(i);
4869 if (includeCurrentProfiles) {
4870 if (!notificationMatchesCurrentProfiles(r, userId)) {
4873 } else if (!notificationMatchesUserId(r, userId)) {
4876 // Don't remove notifications to all, if there's no package name specified
4877 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4880 if (!flagChecker.apply(r.getFlags())) {
4883 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4886 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4889 if (canceledNotifications == null) {
4890 canceledNotifications = new ArrayList<>();
4892 notificationList.remove(i);
4893 mNotificationsByKey.remove(r.getKey());
4894 canceledNotifications.add(r);
4895 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4897 if (canceledNotifications != null) {
4898 final int M = canceledNotifications.size();
4899 for (int i = 0; i < M; i++) {
4900 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4901 listenerName, false /* sendDelete */, flagChecker);
4903 updateLightsLocked();
4907 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4908 ManagedServiceInfo listener) {
4909 String listenerName = listener == null ? null : listener.component.toShortString();
4910 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4915 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4916 snoozeCriterionId, listenerName));
4918 // Needs to post so that it can cancel notifications not yet enqueued.
4919 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4922 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4923 String listenerName = listener == null ? null : listener.component.toShortString();
4925 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4927 mSnoozeHelper.repost(key);
4931 @GuardedBy("mNotificationLock")
4932 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4933 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4934 mHandler.post(new Runnable() {
4937 synchronized (mNotificationLock) {
4938 String listenerName =
4939 listener == null ? null : listener.component.toShortString();
4940 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4941 null, userId, 0, 0, reason, listenerName);
4943 FlagChecker flagChecker = (int flags) -> {
4944 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4951 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4952 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4953 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4954 listenerName, true);
4955 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4956 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4957 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4958 reason, listenerName, false);
4959 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4965 // Warning: The caller is responsible for invoking updateLightsLocked().
4966 @GuardedBy("mNotificationLock")
4967 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4968 String listenerName, boolean sendDelete, FlagChecker flagChecker) {
4969 Notification n = r.getNotification();
4970 if (!n.isGroupSummary()) {
4974 String pkg = r.sbn.getPackageName();
4977 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4981 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4982 sendDelete, true, flagChecker);
4983 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4984 listenerName, sendDelete, false, flagChecker);
4987 @GuardedBy("mNotificationLock")
4988 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4989 NotificationRecord parentNotification, int callingUid, int callingPid,
4990 String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
4991 final String pkg = parentNotification.sbn.getPackageName();
4992 final int userId = parentNotification.getUserId();
4993 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4994 for (int i = notificationList.size() - 1; i >= 0; i--) {
4995 final NotificationRecord childR = notificationList.get(i);
4996 final StatusBarNotification childSbn = childR.sbn;
4997 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4998 childR.getGroupKey().equals(parentNotification.getGroupKey())
4999 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
5000 && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
5001 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
5002 childSbn.getTag(), userId, 0, 0, reason, listenerName);
5003 notificationList.remove(i);
5004 mNotificationsByKey.remove(childR.getKey());
5005 cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName);
5010 @GuardedBy("mNotificationLock")
5011 void updateLightsLocked()
5013 // handle notification lights
5014 NotificationRecord ledNotification = null;
5015 while (ledNotification == null && !mLights.isEmpty()) {
5016 final String owner = mLights.get(mLights.size() - 1);
5017 ledNotification = mNotificationsByKey.get(owner);
5018 if (ledNotification == null) {
5019 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
5020 mLights.remove(owner);
5024 // Don't flash while we are in a call or screen is on
5025 if (ledNotification == null || mInCall || mScreenOn) {
5026 mNotificationLight.turnOff();
5028 NotificationRecord.Light light = ledNotification.getLight();
5029 if (light != null && mNotificationPulseEnabled) {
5031 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
5032 light.onMs, light.offMs);
5037 @GuardedBy("mNotificationLock")
5038 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
5039 String groupKey, int userId) {
5040 List<NotificationRecord> records = new ArrayList<>();
5041 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
5043 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
5048 @GuardedBy("mNotificationLock")
5049 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
5050 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
5051 List<NotificationRecord> records = new ArrayList<>();
5052 final int len = list.size();
5053 for (int i = 0; i < len; i++) {
5054 NotificationRecord r = list.get(i);
5055 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
5056 && r.sbn.getPackageName().equals(pkg)) {
5063 // Searches both enqueued and posted notifications by key.
5064 // TODO: need to combine a bunch of these getters with slightly different behavior.
5065 // TODO: Should enqueuing just add to mNotificationsByKey instead?
5066 @GuardedBy("mNotificationLock")
5067 private NotificationRecord findNotificationByKeyLocked(String key) {
5068 NotificationRecord r;
5069 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
5072 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
5078 @GuardedBy("mNotificationLock")
5079 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
5080 NotificationRecord r;
5081 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
5084 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
5091 @GuardedBy("mNotificationLock")
5092 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5093 String pkg, String tag, int id, int userId) {
5094 final int len = list.size();
5095 for (int i = 0; i < len; i++) {
5096 NotificationRecord r = list.get(i);
5097 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
5098 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
5105 @GuardedBy("mNotificationLock")
5106 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5108 final int N = list.size();
5109 for (int i = 0; i < N; i++) {
5110 if (key.equals(list.get(i).getKey())) {
5117 @GuardedBy("mNotificationLock")
5118 int indexOfNotificationLocked(String key) {
5119 final int N = mNotificationList.size();
5120 for (int i = 0; i < N; i++) {
5121 if (key.equals(mNotificationList.get(i).getKey())) {
5128 private void updateNotificationPulse() {
5129 synchronized (mNotificationLock) {
5130 updateLightsLocked();
5134 protected boolean isCallingUidSystem() {
5135 final int uid = Binder.getCallingUid();
5136 return uid == Process.SYSTEM_UID;
5139 protected boolean isUidSystemOrPhone(int uid) {
5140 final int appid = UserHandle.getAppId(uid);
5141 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
5144 // TODO: Most calls should probably move to isCallerSystem.
5145 protected boolean isCallerSystemOrPhone() {
5146 return isUidSystemOrPhone(Binder.getCallingUid());
5149 private void checkCallerIsSystemOrShell() {
5150 if (Binder.getCallingUid() == Process.SHELL_UID) {
5153 checkCallerIsSystem();
5156 private void checkCallerIsSystem() {
5157 if (isCallerSystemOrPhone()) {
5160 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
5163 private void checkCallerIsSystemOrSameApp(String pkg) {
5164 if (isCallerSystemOrPhone()) {
5167 checkCallerIsSameApp(pkg);
5170 private boolean isCallerInstantApp(String pkg) {
5171 // System is always allowed to act for ephemeral apps.
5172 if (isCallerSystemOrPhone()) {
5176 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
5179 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
5180 UserHandle.getCallingUserId());
5182 throw new SecurityException("Unknown package " + pkg);
5184 return ai.isInstantApp();
5185 } catch (RemoteException re) {
5186 throw new SecurityException("Unknown package " + pkg, re);
5191 private void checkCallerIsSameApp(String pkg) {
5192 final int uid = Binder.getCallingUid();
5194 ApplicationInfo ai = mPackageManager.getApplicationInfo(
5195 pkg, 0, UserHandle.getCallingUserId());
5197 throw new SecurityException("Unknown package " + pkg);
5199 if (!UserHandle.isSameApp(ai.uid, uid)) {
5200 throw new SecurityException("Calling uid " + uid + " gave package "
5201 + pkg + " which is owned by uid " + ai.uid);
5203 } catch (RemoteException re) {
5204 throw new SecurityException("Unknown package " + pkg + "\n" + re);
5208 private static String callStateToString(int state) {
5210 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
5211 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
5212 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
5213 default: return "CALL_STATE_UNKNOWN_" + state;
5217 private void listenForCallState() {
5218 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
5220 public void onCallStateChanged(int state, String incomingNumber) {
5221 if (mCallState == state) return;
5222 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
5225 }, PhoneStateListener.LISTEN_CALL_STATE);
5229 * Generates a NotificationRankingUpdate from 'sbns', considering only
5230 * notifications visible to the given listener.
5232 @GuardedBy("mNotificationLock")
5233 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
5234 final int N = mNotificationList.size();
5235 ArrayList<String> keys = new ArrayList<String>(N);
5236 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
5237 ArrayList<Integer> importance = new ArrayList<>(N);
5238 Bundle overrideGroupKeys = new Bundle();
5239 Bundle visibilityOverrides = new Bundle();
5240 Bundle suppressedVisualEffects = new Bundle();
5241 Bundle explanation = new Bundle();
5242 Bundle channels = new Bundle();
5243 Bundle overridePeople = new Bundle();
5244 Bundle snoozeCriteria = new Bundle();
5245 Bundle showBadge = new Bundle();
5246 for (int i = 0; i < N; i++) {
5247 NotificationRecord record = mNotificationList.get(i);
5248 if (!isVisibleToListener(record.sbn, info)) {
5251 final String key = record.sbn.getKey();
5253 importance.add(record.getImportance());
5254 if (record.getImportanceExplanation() != null) {
5255 explanation.putCharSequence(key, record.getImportanceExplanation());
5257 if (record.isIntercepted()) {
5258 interceptedKeys.add(key);
5261 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
5262 if (record.getPackageVisibilityOverride()
5263 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
5264 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
5266 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
5267 channels.putParcelable(key, record.getChannel());
5268 overridePeople.putStringArrayList(key, record.getPeopleOverride());
5269 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
5270 showBadge.putBoolean(key, record.canShowBadge());
5272 final int M = keys.size();
5273 String[] keysAr = keys.toArray(new String[M]);
5274 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
5275 int[] importanceAr = new int[M];
5276 for (int i = 0; i < M; i++) {
5277 importanceAr[i] = importance.get(i);
5279 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
5280 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
5281 channels, overridePeople, snoozeCriteria, showBadge);
5284 boolean hasCompanionDevice(ManagedServiceInfo info) {
5285 if (mCompanionManager == null) {
5286 mCompanionManager = getCompanionManager();
5288 // Companion mgr doesn't exist on all device types
5289 if (mCompanionManager == null) {
5292 long identity = Binder.clearCallingIdentity();
5294 List<String> associations = mCompanionManager.getAssociations(
5295 info.component.getPackageName(), info.userid);
5296 if (!ArrayUtils.isEmpty(associations)) {
5299 } catch (SecurityException se) {
5300 // Not a privileged listener
5301 } catch (RemoteException re) {
5302 Slog.e(TAG, "Cannot reach companion device service", re);
5303 } catch (Exception e) {
5304 Slog.e(TAG, "Cannot verify listener " + info, e);
5306 Binder.restoreCallingIdentity(identity);
5311 protected ICompanionDeviceManager getCompanionManager() {
5312 return ICompanionDeviceManager.Stub.asInterface(
5313 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
5316 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5317 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5320 // TODO: remove this for older listeners.
5324 private boolean isPackageSuspendedForUser(String pkg, int uid) {
5325 int userId = UserHandle.getUserId(uid);
5327 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
5328 } catch (RemoteException re) {
5329 throw new SecurityException("Could not talk to package manager service");
5330 } catch (IllegalArgumentException ex) {
5331 // Package not found.
5336 private class TrimCache {
5337 StatusBarNotification heavy;
5338 StatusBarNotification sbnClone;
5339 StatusBarNotification sbnCloneLight;
5341 TrimCache(StatusBarNotification sbn) {
5345 StatusBarNotification ForListener(ManagedServiceInfo info) {
5346 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5347 if (sbnCloneLight == null) {
5348 sbnCloneLight = heavy.cloneLight();
5350 return sbnCloneLight;
5352 if (sbnClone == null) {
5353 sbnClone = heavy.clone();
5360 public class NotificationAssistants extends ManagedServices {
5361 static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
5363 public NotificationAssistants(IPackageManager pm) {
5364 super(getContext(), mNotificationLock, mUserProfiles, pm);
5368 protected Config getConfig() {
5369 Config c = new Config();
5370 c.caption = "notification assistant service";
5371 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5372 c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
5373 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5374 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5375 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5376 c.clientLabel = R.string.notification_ranker_binding_label;
5381 protected IInterface asInterface(IBinder binder) {
5382 return INotificationListener.Stub.asInterface(binder);
5386 protected boolean checkType(IInterface service) {
5387 return service instanceof INotificationListener;
5391 protected void onServiceAdded(ManagedServiceInfo info) {
5392 mListeners.registerGuestService(info);
5396 @GuardedBy("mNotificationLock")
5397 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5398 mListeners.unregisterService(removed.service, removed.userid);
5401 public void onNotificationEnqueued(final NotificationRecord r) {
5402 final StatusBarNotification sbn = r.sbn;
5403 TrimCache trimCache = new TrimCache(sbn);
5405 // There should be only one, but it's a list, so while we enforce
5406 // singularity elsewhere, we keep it general here, to avoid surprises.
5407 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5408 boolean sbnVisible = isVisibleToListener(sbn, info);
5413 final int importance = r.getImportance();
5414 final boolean fromUser = r.isImportanceFromUser();
5415 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5416 mHandler.post(new Runnable() {
5419 notifyEnqueued(info, sbnToPost);
5425 private void notifyEnqueued(final ManagedServiceInfo info,
5426 final StatusBarNotification sbn) {
5427 final INotificationListener assistant = (INotificationListener) info.service;
5428 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5430 assistant.onNotificationEnqueued(sbnHolder);
5431 } catch (RemoteException ex) {
5432 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5437 * asynchronously notify the assistant that a notification has been snoozed until a
5440 @GuardedBy("mNotificationLock")
5441 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5442 final String snoozeCriterionId) {
5443 TrimCache trimCache = new TrimCache(sbn);
5444 for (final ManagedServiceInfo info : getServices()) {
5445 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5446 mHandler.post(new Runnable() {
5449 final INotificationListener assistant =
5450 (INotificationListener) info.service;
5451 StatusBarNotificationHolder sbnHolder
5452 = new StatusBarNotificationHolder(sbnToPost);
5454 assistant.onNotificationSnoozedUntilContext(
5455 sbnHolder, snoozeCriterionId);
5456 } catch (RemoteException ex) {
5457 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5464 public boolean isEnabled() {
5465 return !getServices().isEmpty();
5469 public class NotificationListeners extends ManagedServices {
5470 static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
5472 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5474 public NotificationListeners(IPackageManager pm) {
5475 super(getContext(), mNotificationLock, mUserProfiles, pm);
5480 protected Config getConfig() {
5481 Config c = new Config();
5482 c.caption = "notification listener";
5483 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5484 c.xmlTag = TAG_ENABLED_NOTIFICATION_LISTENERS;
5485 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5486 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5487 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5488 c.clientLabel = R.string.notification_listener_binding_label;
5493 protected IInterface asInterface(IBinder binder) {
5494 return INotificationListener.Stub.asInterface(binder);
5498 protected boolean checkType(IInterface service) {
5499 return service instanceof INotificationListener;
5503 public void onServiceAdded(ManagedServiceInfo info) {
5504 final INotificationListener listener = (INotificationListener) info.service;
5505 final NotificationRankingUpdate update;
5506 synchronized (mNotificationLock) {
5507 update = makeRankingUpdateLocked(info);
5510 listener.onListenerConnected(update);
5511 } catch (RemoteException e) {
5517 @GuardedBy("mNotificationLock")
5518 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5519 if (removeDisabledHints(removed)) {
5520 updateListenerHintsLocked();
5521 updateEffectsSuppressorLocked();
5523 mLightTrimListeners.remove(removed);
5526 @GuardedBy("mNotificationLock")
5527 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5528 if (trim == TRIM_LIGHT) {
5529 mLightTrimListeners.add(info);
5531 mLightTrimListeners.remove(info);
5535 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5536 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5540 * asynchronously notify all listeners about a new notification
5543 * Also takes care of removing a notification that has been visible to a listener before,
5544 * but isn't anymore.
5546 @GuardedBy("mNotificationLock")
5547 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5548 // Lazily initialized snapshots of the notification.
5549 TrimCache trimCache = new TrimCache(sbn);
5551 for (final ManagedServiceInfo info : getServices()) {
5552 boolean sbnVisible = isVisibleToListener(sbn, info);
5553 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5554 // This notification hasn't been and still isn't visible -> ignore.
5555 if (!oldSbnVisible && !sbnVisible) {
5558 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5560 // This notification became invisible -> remove the old one.
5561 if (oldSbnVisible && !sbnVisible) {
5562 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5563 mHandler.post(new Runnable() {
5566 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5572 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5573 mHandler.post(new Runnable() {
5576 notifyPosted(info, sbnToPost, update);
5583 * asynchronously notify all listeners about a removed notification
5585 @GuardedBy("mNotificationLock")
5586 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5587 // make a copy in case changes are made to the underlying Notification object
5588 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5590 final StatusBarNotification sbnLight = sbn.cloneLight();
5591 for (final ManagedServiceInfo info : getServices()) {
5592 if (!isVisibleToListener(sbn, info)) {
5595 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5596 mHandler.post(new Runnable() {
5599 notifyRemoved(info, sbnLight, update, reason);
5606 * asynchronously notify all listeners about a reordering of notifications
5608 @GuardedBy("mNotificationLock")
5609 public void notifyRankingUpdateLocked() {
5610 for (final ManagedServiceInfo serviceInfo : getServices()) {
5611 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5614 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5615 mHandler.post(new Runnable() {
5618 notifyRankingUpdate(serviceInfo, update);
5624 @GuardedBy("mNotificationLock")
5625 public void notifyListenerHintsChangedLocked(final int hints) {
5626 for (final ManagedServiceInfo serviceInfo : getServices()) {
5627 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5630 mHandler.post(new Runnable() {
5633 notifyListenerHintsChanged(serviceInfo, hints);
5639 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5640 for (final ManagedServiceInfo serviceInfo : getServices()) {
5641 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5644 mHandler.post(new Runnable() {
5647 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5653 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5654 final NotificationChannel channel, final int modificationType) {
5655 if (channel == null) {
5658 for (final ManagedServiceInfo serviceInfo : getServices()) {
5659 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5663 BackgroundThread.getHandler().post(() -> {
5664 if (hasCompanionDevice(serviceInfo)) {
5665 notifyNotificationChannelChanged(
5666 serviceInfo, pkg, user, channel, modificationType);
5672 protected void notifyNotificationChannelGroupChanged(
5673 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5674 final int modificationType) {
5675 if (group == null) {
5678 for (final ManagedServiceInfo serviceInfo : getServices()) {
5679 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5683 BackgroundThread.getHandler().post(() -> {
5684 if (hasCompanionDevice(serviceInfo)) {
5685 notifyNotificationChannelGroupChanged(
5686 serviceInfo, pkg, user, group, modificationType);
5692 private void notifyPosted(final ManagedServiceInfo info,
5693 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5694 final INotificationListener listener = (INotificationListener) info.service;
5695 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5697 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5698 } catch (RemoteException ex) {
5699 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5703 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5704 NotificationRankingUpdate rankingUpdate, int reason) {
5705 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5708 final INotificationListener listener = (INotificationListener) info.service;
5709 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5711 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5712 } catch (RemoteException ex) {
5713 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5717 private void notifyRankingUpdate(ManagedServiceInfo info,
5718 NotificationRankingUpdate rankingUpdate) {
5719 final INotificationListener listener = (INotificationListener) info.service;
5721 listener.onNotificationRankingUpdate(rankingUpdate);
5722 } catch (RemoteException ex) {
5723 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5727 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5728 final INotificationListener listener = (INotificationListener) info.service;
5730 listener.onListenerHintsChanged(hints);
5731 } catch (RemoteException ex) {
5732 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5736 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5737 int interruptionFilter) {
5738 final INotificationListener listener = (INotificationListener) info.service;
5740 listener.onInterruptionFilterChanged(interruptionFilter);
5741 } catch (RemoteException ex) {
5742 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5746 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5747 final String pkg, final UserHandle user, final NotificationChannel channel,
5748 final int modificationType) {
5749 final INotificationListener listener = (INotificationListener) info.service;
5751 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5752 } catch (RemoteException ex) {
5753 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5757 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5758 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5759 final int modificationType) {
5760 final INotificationListener listener = (INotificationListener) info.service;
5762 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5763 } catch (RemoteException ex) {
5764 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5768 public boolean isListenerPackage(String packageName) {
5769 if (packageName == null) {
5772 // TODO: clean up locking object later
5773 synchronized (mNotificationLock) {
5774 for (final ManagedServiceInfo serviceInfo : getServices()) {
5775 if (packageName.equals(serviceInfo.component.getPackageName())) {
5784 public static final class DumpFilter {
5785 public boolean filtered = false;
5786 public String pkgFilter;
5789 public boolean stats;
5790 public boolean redact = true;
5791 public boolean proto = false;
5793 public static DumpFilter parseFromArguments(String[] args) {
5794 final DumpFilter filter = new DumpFilter();
5795 for (int ai = 0; ai < args.length; ai++) {
5796 final String a = args[ai];
5797 if ("--proto".equals(args[0])) {
5798 filter.proto = true;
5800 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5801 filter.redact = false;
5802 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5803 if (ai < args.length-1) {
5805 filter.pkgFilter = args[ai].trim().toLowerCase();
5806 if (filter.pkgFilter.isEmpty()) {
5807 filter.pkgFilter = null;
5809 filter.filtered = true;
5812 } else if ("--zen".equals(a) || "zen".equals(a)) {
5813 filter.filtered = true;
5815 } else if ("--stats".equals(a)) {
5816 filter.stats = true;
5817 if (ai < args.length-1) {
5819 filter.since = Long.parseLong(args[ai]);
5828 public boolean matches(StatusBarNotification sbn) {
5829 if (!filtered) return true;
5830 return zen ? true : sbn != null
5831 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5834 public boolean matches(ComponentName component) {
5835 if (!filtered) return true;
5836 return zen ? true : component != null && matches(component.getPackageName());
5839 public boolean matches(String pkg) {
5840 if (!filtered) return true;
5841 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5845 public String toString() {
5846 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5851 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5852 * binder without sending large amounts of data over a oneway transaction.
5854 private static final class StatusBarNotificationHolder
5855 extends IStatusBarNotificationHolder.Stub {
5856 private StatusBarNotification mValue;
5858 public StatusBarNotificationHolder(StatusBarNotification value) {
5862 /** Get the held value and clear it. This function should only be called once per holder */
5864 public StatusBarNotification get() {
5865 StatusBarNotification value = mValue;
5871 private class ShellCmd extends ShellCommand {
5872 public static final String USAGE = "help\n"
5873 + "allow_listener COMPONENT [user_id]\n"
5874 + "disallow_listener COMPONENT [user_id]\n"
5875 + "set_assistant COMPONENT\n"
5876 + "remove_assistant COMPONENT\n"
5877 + "allow_dnd PACKAGE\n"
5878 + "disallow_dnd PACKAGE";
5881 public int onCommand(String cmd) {
5883 return handleDefaultCommands(cmd);
5885 final PrintWriter pw = getOutPrintWriter();
5889 getBinderService().setNotificationPolicyAccessGranted(
5890 getNextArgRequired(), true);
5894 case "disallow_dnd": {
5895 getBinderService().setNotificationPolicyAccessGranted(
5896 getNextArgRequired(), false);
5899 case "allow_listener": {
5900 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5902 pw.println("Invalid listener - must be a ComponentName");
5905 String userId = getNextArg();
5906 if (userId == null) {
5907 getBinderService().setNotificationListenerAccessGranted(cn, true);
5909 getBinderService().setNotificationListenerAccessGrantedForUser(
5910 cn, Integer.parseInt(userId), true);
5914 case "disallow_listener": {
5915 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5917 pw.println("Invalid listener - must be a ComponentName");
5920 String userId = getNextArg();
5921 if (userId == null) {
5922 getBinderService().setNotificationListenerAccessGranted(cn, false);
5924 getBinderService().setNotificationListenerAccessGrantedForUser(
5925 cn, Integer.parseInt(userId), false);
5929 case "allow_assistant": {
5930 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5932 pw.println("Invalid assistant - must be a ComponentName");
5935 getBinderService().setNotificationAssistantAccessGranted(cn, true);
5938 case "disallow_assistant": {
5939 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5941 pw.println("Invalid assistant - must be a ComponentName");
5944 getBinderService().setNotificationAssistantAccessGranted(cn, false);
5949 return handleDefaultCommands(cmd);
5951 } catch (Exception e) {
5952 pw.println("Error occurred. Check logcat for details. " + e.getMessage());
5953 Slog.e(TAG, "Error running shell command", e);
5959 public void onHelp() {
5960 getOutPrintWriter().println(USAGE);