2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.server.notification;
19 import static android.app.NotificationManager.IMPORTANCE_MIN;
20 import static android.app.NotificationManager.IMPORTANCE_NONE;
21 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
22 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
23 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
24 import static android.os.UserHandle.USER_NULL;
25 import static android.service.notification.NotificationListenerService
26 .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
27 import static android.service.notification.NotificationListenerService
28 .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
29 import static android.service.notification.NotificationListenerService
30 .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
31 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
32 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
33 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
34 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
35 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
36 import static android.service.notification.NotificationListenerService.REASON_CLICK;
37 import static android.service.notification.NotificationListenerService.REASON_ERROR;
38 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
39 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
40 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
41 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
42 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
43 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
44 import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
45 import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
46 import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
47 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
48 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
49 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
50 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
51 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
52 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
53 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
54 import static android.service.notification.NotificationListenerService.TRIM_FULL;
55 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
57 import static android.view.Display.DEFAULT_DISPLAY;
58 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
60 import android.Manifest;
61 import android.annotation.NonNull;
62 import android.annotation.Nullable;
63 import android.app.ActivityManager;
64 import android.app.ActivityManagerInternal;
65 import android.app.AlarmManager;
66 import android.app.AppGlobals;
67 import android.app.AppOpsManager;
68 import android.app.AutomaticZenRule;
69 import android.app.NotificationChannelGroup;
70 import android.app.backup.BackupManager;
71 import android.app.IActivityManager;
72 import android.app.INotificationManager;
73 import android.app.ITransientNotification;
74 import android.app.Notification;
75 import android.app.NotificationChannel;
76 import android.app.NotificationManager.Policy;
77 import android.app.NotificationManager;
78 import android.app.PendingIntent;
79 import android.app.StatusBarManager;
80 import android.app.usage.UsageEvents;
81 import android.app.usage.UsageStatsManagerInternal;
82 import android.companion.ICompanionDeviceManager;
83 import android.content.BroadcastReceiver;
84 import android.content.ComponentName;
85 import android.content.ContentResolver;
86 import android.content.Context;
87 import android.content.Intent;
88 import android.content.IntentFilter;
89 import android.content.pm.ApplicationInfo;
90 import android.content.pm.IPackageManager;
91 import android.content.pm.PackageManager;
92 import android.content.pm.PackageManager.NameNotFoundException;
93 import android.content.pm.ParceledListSlice;
94 import android.content.res.Resources;
95 import android.database.ContentObserver;
96 import android.media.AudioManager;
97 import android.media.AudioManagerInternal;
98 import android.media.IRingtonePlayer;
99 import android.media.ToneGenerator;
100 import android.net.Uri;
101 import android.os.Binder;
102 import android.os.Build;
103 import android.os.Bundle;
104 import android.os.Environment;
105 import android.os.Handler;
106 import android.os.HandlerThread;
107 import android.os.IBinder;
108 import android.os.IInterface;
109 import android.os.Looper;
110 import android.os.Message;
111 import android.os.Process;
112 import android.os.RemoteException;
113 import android.os.ResultReceiver;
114 import android.os.ServiceManager;
115 import android.os.ShellCallback;
116 import android.os.ShellCommand;
117 import android.os.SystemClock;
118 import android.os.SystemProperties;
119 import android.os.UserHandle;
120 import android.os.Vibrator;
121 import android.os.VibrationEffect;
122 import android.provider.Settings;
123 import android.service.notification.Adjustment;
124 import android.service.notification.Condition;
125 import android.service.notification.IConditionProvider;
126 import android.service.notification.INotificationListener;
127 import android.service.notification.IStatusBarNotificationHolder;
128 import android.service.notification.NotificationAssistantService;
129 import android.service.notification.NotificationListenerService;
130 import android.service.notification.NotificationRankingUpdate;
131 import android.service.notification.NotificationRecordProto;
132 import android.service.notification.NotificationServiceDumpProto;
133 import android.service.notification.NotificationServiceProto;
134 import android.service.notification.SnoozeCriterion;
135 import android.service.notification.StatusBarNotification;
136 import android.service.notification.ZenModeConfig;
137 import android.service.notification.ZenModeProto;
138 import android.telecom.TelecomManager;
139 import android.telephony.PhoneStateListener;
140 import android.telephony.TelephonyManager;
141 import android.text.TextUtils;
142 import android.util.ArrayMap;
143 import android.util.ArraySet;
144 import android.util.AtomicFile;
145 import android.util.Log;
146 import android.util.Slog;
147 import android.util.SparseArray;
148 import android.util.Xml;
149 import android.util.proto.ProtoOutputStream;
150 import android.view.WindowManagerInternal;
151 import android.view.accessibility.AccessibilityEvent;
152 import android.view.accessibility.AccessibilityManager;
153 import android.widget.Toast;
155 import com.android.internal.R;
156 import com.android.internal.annotations.GuardedBy;
157 import com.android.internal.annotations.VisibleForTesting;
158 import com.android.internal.logging.MetricsLogger;
159 import com.android.internal.logging.nano.MetricsProto;
160 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
161 import com.android.internal.statusbar.NotificationVisibility;
162 import com.android.internal.util.ArrayUtils;
163 import com.android.internal.util.DumpUtils;
164 import com.android.internal.util.FastXmlSerializer;
165 import com.android.internal.util.Preconditions;
166 import com.android.internal.util.XmlUtils;
167 import com.android.server.DeviceIdleController;
168 import com.android.server.EventLogTags;
169 import com.android.server.LocalServices;
170 import com.android.server.SystemService;
171 import com.android.server.lights.Light;
172 import com.android.server.lights.LightsManager;
173 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
174 import com.android.server.policy.PhoneWindowManager;
175 import com.android.server.statusbar.StatusBarManagerInternal;
176 import com.android.server.notification.ManagedServices.UserProfiles;
178 import libcore.io.IoUtils;
180 import org.json.JSONException;
181 import org.json.JSONObject;
182 import org.xmlpull.v1.XmlPullParser;
183 import org.xmlpull.v1.XmlPullParserException;
184 import org.xmlpull.v1.XmlSerializer;
186 import java.io.ByteArrayInputStream;
187 import java.io.ByteArrayOutputStream;
189 import java.io.FileDescriptor;
190 import java.io.FileInputStream;
191 import java.io.FileNotFoundException;
192 import java.io.FileOutputStream;
193 import java.io.IOException;
194 import java.io.InputStream;
195 import java.io.OutputStream;
196 import java.io.PrintWriter;
197 import java.nio.charset.StandardCharsets;
198 import java.util.ArrayDeque;
199 import java.util.ArrayList;
200 import java.util.Arrays;
201 import java.util.Iterator;
202 import java.util.List;
203 import java.util.Map.Entry;
204 import java.util.Objects;
205 import java.util.Set;
206 import java.util.concurrent.TimeUnit;
209 public class NotificationManagerService extends SystemService {
210 static final String TAG = "NotificationService";
211 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
212 public static final boolean ENABLE_CHILD_NOTIFICATIONS
213 = SystemProperties.getBoolean("debug.child_notifs", true);
215 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
216 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
219 static final int MESSAGE_TIMEOUT = 2;
220 static final int MESSAGE_SAVE_POLICY_FILE = 3;
221 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
222 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
223 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
225 // ranking thread messages
226 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
227 private static final int MESSAGE_RANKING_SORT = 1001;
229 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
230 static final int SHORT_DELAY = 2000; // 2 seconds
232 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
234 static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
236 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
238 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
240 static final boolean ENABLE_BLOCKED_TOASTS = true;
242 // When #matchesCallFilter is called from the ringer, wait at most
243 // 3s to resolve the contacts. This timeout is required since
244 // ContactsProvider might take a long time to start up.
246 // Return STARRED_CONTACT when the timeout is hit in order to avoid
247 // missed calls in ZEN mode "Important".
248 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
249 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
250 ValidateNotificationPeople.STARRED_CONTACT;
252 /** notification_enqueue status value for a newly enqueued notification. */
253 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
255 /** notification_enqueue status value for an existing notification. */
256 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
258 /** notification_enqueue status value for an ignored notification. */
259 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
260 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
262 private static final long DELAY_FOR_ASSISTANT_TIME = 100;
264 private static final String ACTION_NOTIFICATION_TIMEOUT =
265 NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
266 private static final int REQUEST_CODE_TIMEOUT = 1;
267 private static final String SCHEME_TIMEOUT = "timeout";
268 private static final String EXTRA_KEY = "key";
270 private IActivityManager mAm;
271 private ActivityManager mActivityManager;
272 private IPackageManager mPackageManager;
273 private PackageManager mPackageManagerClient;
274 AudioManager mAudioManager;
275 AudioManagerInternal mAudioManagerInternal;
276 @Nullable StatusBarManagerInternal mStatusBar;
278 private WindowManagerInternal mWindowManagerInternal;
279 private AlarmManager mAlarmManager;
280 private ICompanionDeviceManager mCompanionManager;
282 final IBinder mForegroundToken = new Binder();
283 private WorkerHandler mHandler;
284 private final HandlerThread mRankingThread = new HandlerThread("ranker",
285 Process.THREAD_PRIORITY_BACKGROUND);
287 private Light mNotificationLight;
288 Light mAttentionLight;
290 private long[] mFallbackVibrationPattern;
291 private boolean mUseAttentionLight;
292 boolean mSystemReady;
294 private boolean mDisableNotificationEffects;
295 private int mCallState;
296 private String mSoundNotificationKey;
297 private String mVibrateNotificationKey;
299 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
301 private List<ComponentName> mEffectsSuppressors = new ArrayList<>();
302 private int mListenerHints; // right now, all hints are global
303 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
305 // for enabling and disabling notification pulse behavior
306 private boolean mScreenOn = true;
307 private boolean mInCall = false;
308 private boolean mNotificationPulseEnabled;
310 // for generating notification tones in-call
311 private ToneGenerator mInCallToneGenerator;
312 private final Object mInCallToneGeneratorLock = new Object();
314 // used as a mutex for access to all active notifications & listeners
315 final Object mNotificationLock = new Object();
316 @GuardedBy("mNotificationLock")
317 final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();
318 @GuardedBy("mNotificationLock")
319 final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();
320 @GuardedBy("mNotificationLock")
321 final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
322 @GuardedBy("mNotificationLock")
323 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
324 final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
325 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
327 // The last key in this list owns the hardware.
328 ArrayList<String> mLights = new ArrayList<>();
330 private AppOpsManager mAppOps;
331 private UsageStatsManagerInternal mAppUsageStats;
333 private Archive mArchive;
335 // Persistent storage for notification policy
336 private AtomicFile mPolicyFile;
338 private static final int DB_VERSION = 1;
340 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
341 private static final String ATTR_VERSION = "version";
343 private RankingHelper mRankingHelper;
345 private final UserProfiles mUserProfiles = new UserProfiles();
346 private NotificationListeners mListeners;
347 private NotificationAssistants mAssistants;
348 private ConditionProviders mConditionProviders;
349 private NotificationUsageStats mUsageStats;
351 private static final int MY_UID = Process.myUid();
352 private static final int MY_PID = Process.myPid();
353 private static final IBinder WHITELIST_TOKEN = new Binder();
354 private RankingHandler mRankingHandler;
355 private long mLastOverRateLogTime;
356 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
358 private SnoozeHelper mSnoozeHelper;
359 private GroupHelper mGroupHelper;
360 private boolean mIsTelevision;
362 private static class Archive {
363 final int mBufferSize;
364 final ArrayDeque<StatusBarNotification> mBuffer;
366 public Archive(int size) {
368 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
371 public String toString() {
372 final StringBuilder sb = new StringBuilder();
373 final int N = mBuffer.size();
374 sb.append("Archive (");
376 sb.append(" notification");
377 sb.append((N==1)?")":"s)");
378 return sb.toString();
381 public void record(StatusBarNotification nr) {
382 if (mBuffer.size() == mBufferSize) {
383 mBuffer.removeFirst();
386 // We don't want to store the heavy bits of the notification in the archive,
387 // but other clients in the system process might be using the object, so we
388 // store a (lightened) copy.
389 mBuffer.addLast(nr.cloneLight());
392 public Iterator<StatusBarNotification> descendingIterator() {
393 return mBuffer.descendingIterator();
396 public StatusBarNotification[] getArray(int count) {
397 if (count == 0) count = mBufferSize;
398 final StatusBarNotification[] a
399 = new StatusBarNotification[Math.min(count, mBuffer.size())];
400 Iterator<StatusBarNotification> iter = descendingIterator();
402 while (iter.hasNext() && i < count) {
403 a[i++] = iter.next();
410 protected void readDefaultApprovedServices(int userId) {
411 String defaultListenerAccess = getContext().getResources().getString(
412 com.android.internal.R.string.config_defaultListenerAccessPackages);
413 if (defaultListenerAccess != null) {
414 for (String whitelisted :
415 defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
416 // Gather all notification listener components for candidate pkgs.
417 Set<ComponentName> approvedListeners =
418 mListeners.queryPackageForServices(whitelisted,
419 PackageManager.MATCH_DIRECT_BOOT_AWARE
420 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
421 for (ComponentName cn : approvedListeners) {
423 getBinderService().setNotificationListenerAccessGrantedForUser(cn,
425 } catch (RemoteException e) {
431 String defaultDndAccess = getContext().getResources().getString(
432 com.android.internal.R.string.config_defaultDndAccessPackages);
433 if (defaultListenerAccess != null) {
434 for (String whitelisted :
435 defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
437 getBinderService().setNotificationPolicyAccessGranted(whitelisted, true);
438 } catch (RemoteException e) {
445 void readPolicyXml(InputStream stream, boolean forRestore)
446 throws XmlPullParserException, NumberFormatException, IOException {
447 final XmlPullParser parser = Xml.newPullParser();
448 parser.setInput(stream, StandardCharsets.UTF_8.name());
449 XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY);
450 boolean migratedManagedServices = false;
451 int outerDepth = parser.getDepth();
452 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
453 if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
454 mZenModeHelper.readXml(parser, forRestore);
455 } else if (RankingHelper.TAG_RANKING.equals(parser.getName())){
456 mRankingHelper.readXml(parser, forRestore);
458 // No non-system managed services are allowed on low ram devices
459 if (!ActivityManager.isLowRamDeviceStatic()) {
460 if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
461 mListeners.readXml(parser);
462 migratedManagedServices = true;
463 } else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) {
464 mAssistants.readXml(parser);
465 migratedManagedServices = true;
466 } else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) {
467 mConditionProviders.readXml(parser);
468 migratedManagedServices = true;
473 if (!migratedManagedServices) {
474 mListeners.migrateToXml();
475 mAssistants.migrateToXml();
476 mConditionProviders.migrateToXml();
481 private void loadPolicyFile() {
482 if (DBG) Slog.d(TAG, "loadPolicyFile");
483 synchronized (mPolicyFile) {
485 InputStream infile = null;
487 infile = mPolicyFile.openRead();
488 readPolicyXml(infile, false /*forRestore*/);
489 } catch (FileNotFoundException e) {
491 // Load default managed services approvals
492 readDefaultApprovedServices(UserHandle.USER_SYSTEM);
493 } catch (IOException e) {
494 Log.wtf(TAG, "Unable to read notification policy", e);
495 } catch (NumberFormatException e) {
496 Log.wtf(TAG, "Unable to parse notification policy", e);
497 } catch (XmlPullParserException e) {
498 Log.wtf(TAG, "Unable to parse notification policy", e);
500 IoUtils.closeQuietly(infile);
505 public void savePolicyFile() {
506 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
507 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
510 private void handleSavePolicyFile() {
511 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
512 synchronized (mPolicyFile) {
513 final FileOutputStream stream;
515 stream = mPolicyFile.startWrite();
516 } catch (IOException e) {
517 Slog.w(TAG, "Failed to save policy file", e);
522 writePolicyXml(stream, false /*forBackup*/);
523 mPolicyFile.finishWrite(stream);
524 } catch (IOException e) {
525 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
526 mPolicyFile.failWrite(stream);
529 BackupManager.dataChanged(getContext().getPackageName());
532 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
533 final XmlSerializer out = new FastXmlSerializer();
534 out.setOutput(stream, StandardCharsets.UTF_8.name());
535 out.startDocument(null, true);
536 out.startTag(null, TAG_NOTIFICATION_POLICY);
537 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
538 mZenModeHelper.writeXml(out, forBackup);
539 mRankingHelper.writeXml(out, forBackup);
540 mListeners.writeXml(out, forBackup);
541 mAssistants.writeXml(out, forBackup);
542 mConditionProviders.writeXml(out, forBackup);
543 out.endTag(null, TAG_NOTIFICATION_POLICY);
547 /** Use this to check if a package can post a notification or toast. */
548 private boolean checkNotificationOp(String pkg, int uid) {
549 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
550 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
553 private static final class ToastRecord
557 final ITransientNotification callback;
561 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
565 this.callback = callback;
566 this.duration = duration;
570 void update(int duration) {
571 this.duration = duration;
574 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
575 if (filter != null && !filter.matches(pkg)) return;
576 pw.println(prefix + this);
580 public final String toString()
582 return "ToastRecord{"
583 + Integer.toHexString(System.identityHashCode(this))
585 + " callback=" + callback
586 + " duration=" + duration;
591 final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
594 public void onSetDisabled(int status) {
595 synchronized (mNotificationLock) {
596 mDisableNotificationEffects =
597 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
598 if (disableNotificationEffects(null) != null) {
599 // cancel whatever's going on
600 long identity = Binder.clearCallingIdentity();
602 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
603 if (player != null) {
606 } catch (RemoteException e) {
608 Binder.restoreCallingIdentity(identity);
611 identity = Binder.clearCallingIdentity();
615 Binder.restoreCallingIdentity(identity);
622 public void onClearAll(int callingUid, int callingPid, int userId) {
623 synchronized (mNotificationLock) {
624 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
625 /*includeCurrentProfiles*/ true);
630 public void onNotificationClick(int callingUid, int callingPid, String key) {
631 synchronized (mNotificationLock) {
632 NotificationRecord r = mNotificationsByKey.get(key);
634 Log.w(TAG, "No notification with key: " + key);
637 final long now = System.currentTimeMillis();
638 MetricsLogger.action(r.getLogMaker(now)
639 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
640 .setType(MetricsEvent.TYPE_ACTION));
641 EventLogTags.writeNotificationClicked(key,
642 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
644 StatusBarNotification sbn = r.sbn;
645 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
646 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
647 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
653 public void onNotificationActionClick(int callingUid, int callingPid, String key,
655 synchronized (mNotificationLock) {
656 NotificationRecord r = mNotificationsByKey.get(key);
658 Log.w(TAG, "No notification with key: " + key);
661 final long now = System.currentTimeMillis();
662 MetricsLogger.action(r.getLogMaker(now)
663 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
664 .setType(MetricsEvent.TYPE_ACTION)
665 .setSubtype(actionIndex));
666 EventLogTags.writeNotificationActionClicked(key, actionIndex,
667 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
668 // TODO: Log action click via UsageStats.
673 public void onNotificationClear(int callingUid, int callingPid,
674 String pkg, String tag, int id, int userId) {
675 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
676 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
677 true, userId, REASON_CANCEL, null);
681 public void onPanelRevealed(boolean clearEffects, int items) {
682 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
683 MetricsLogger.histogram(getContext(), "note_load", items);
684 EventLogTags.writeNotificationPanelRevealed(items);
691 public void onPanelHidden() {
692 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
693 EventLogTags.writeNotificationPanelHidden();
697 public void clearEffects() {
698 synchronized (mNotificationLock) {
699 if (DBG) Slog.d(TAG, "clearEffects");
701 clearVibrateLocked();
707 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
708 int uid, int initialPid, String message, int userId) {
709 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
710 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
711 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
713 long ident = Binder.clearCallingIdentity();
715 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
716 "Bad notification posted from package " + pkg
718 } catch (RemoteException e) {
720 Binder.restoreCallingIdentity(ident);
724 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
725 NotificationVisibility[] noLongerVisibleKeys) {
726 synchronized (mNotificationLock) {
727 for (NotificationVisibility nv : newlyVisibleKeys) {
728 NotificationRecord r = mNotificationsByKey.get(nv.key);
729 if (r == null) continue;
730 r.setVisibility(true, nv.rank);
733 // Note that we might receive this event after notifications
734 // have already left the system, e.g. after dismissing from the
735 // shade. Hence not finding notifications in
736 // mNotificationsByKey is not an exceptional condition.
737 for (NotificationVisibility nv : noLongerVisibleKeys) {
738 NotificationRecord r = mNotificationsByKey.get(nv.key);
739 if (r == null) continue;
740 r.setVisibility(false, nv.rank);
747 public void onNotificationExpansionChanged(String key,
748 boolean userAction, boolean expanded) {
749 synchronized (mNotificationLock) {
750 NotificationRecord r = mNotificationsByKey.get(key);
752 r.stats.onExpansionChanged(userAction, expanded);
753 final long now = System.currentTimeMillis();
754 MetricsLogger.action(r.getLogMaker(now)
755 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
756 .setType(MetricsEvent.TYPE_DETAIL));
757 EventLogTags.writeNotificationExpansion(key,
758 userAction ? 1 : 0, expanded ? 1 : 0,
759 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
765 @GuardedBy("mNotificationLock")
766 private void clearSoundLocked() {
767 mSoundNotificationKey = null;
768 long identity = Binder.clearCallingIdentity();
770 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
771 if (player != null) {
774 } catch (RemoteException e) {
776 Binder.restoreCallingIdentity(identity);
780 @GuardedBy("mNotificationLock")
781 private void clearVibrateLocked() {
782 mVibrateNotificationKey = null;
783 long identity = Binder.clearCallingIdentity();
787 Binder.restoreCallingIdentity(identity);
791 @GuardedBy("mNotificationLock")
792 private void clearLightsLocked() {
795 updateLightsLocked();
798 protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
800 public void onReceive(Context context, Intent intent) {
801 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
802 mZenModeHelper.updateDefaultZenRules();
807 private final BroadcastReceiver mRestoreReceiver = new BroadcastReceiver() {
809 public void onReceive(Context context, Intent intent) {
810 if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
812 String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
813 String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
814 mListeners.onSettingRestored(element, newValue, getSendingUserId());
815 mConditionProviders.onSettingRestored(element, newValue, getSendingUserId());
816 } catch (Exception e) {
817 Slog.wtf(TAG, "Cannot restore managed services from settings", e);
823 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
825 public void onReceive(Context context, Intent intent) {
826 String action = intent.getAction();
827 if (action == null) {
830 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
831 final NotificationRecord record;
832 synchronized (mNotificationLock) {
833 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
835 if (record != null) {
836 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
837 record.sbn.getPackageName(), record.sbn.getTag(),
838 record.sbn.getId(), 0,
839 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
840 REASON_TIMEOUT, null);
846 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
848 public void onReceive(Context context, Intent intent) {
849 String action = intent.getAction();
850 if (action == null) {
854 boolean queryRestart = false;
855 boolean queryRemove = false;
856 boolean packageChanged = false;
857 boolean cancelNotifications = true;
858 int reason = REASON_PACKAGE_CHANGED;
860 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
861 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
862 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
863 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
864 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
865 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
866 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
867 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
868 UserHandle.USER_ALL);
869 String pkgList[] = null;
870 int uidList[] = null;
871 boolean removingPackage = queryRemove &&
872 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
873 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
874 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
875 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
876 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
877 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
878 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
879 reason = REASON_PACKAGE_SUSPENDED;
880 } else if (queryRestart) {
881 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
882 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
884 Uri uri = intent.getData();
888 String pkgName = uri.getSchemeSpecificPart();
889 if (pkgName == null) {
892 if (packageChanged) {
893 // We cancel notifications for packages which have just been disabled
895 final int enabled = mPackageManager.getApplicationEnabledSetting(
897 changeUserId != UserHandle.USER_ALL ? changeUserId :
898 UserHandle.USER_SYSTEM);
899 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
900 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
901 cancelNotifications = false;
903 } catch (IllegalArgumentException e) {
904 // Package doesn't exist; probably racing with uninstall.
905 // cancelNotifications is already true, so nothing to do here.
907 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
909 } catch (RemoteException e) {
910 // Failed to talk to PackageManagerService Should never happen!
913 pkgList = new String[]{pkgName};
914 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
916 if (pkgList != null && (pkgList.length > 0)) {
917 for (String pkgName : pkgList) {
918 if (cancelNotifications) {
919 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
920 !queryRestart, changeUserId, reason, null);
924 mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
925 mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
926 mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
927 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
933 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
935 public void onReceive(Context context, Intent intent) {
936 String action = intent.getAction();
938 if (action.equals(Intent.ACTION_SCREEN_ON)) {
939 // Keep track of screen on/off state, but do not turn off the notification light
940 // until user passes through the lock screen or views the notification.
942 updateNotificationPulse();
943 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
945 updateNotificationPulse();
946 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
947 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
948 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
949 updateNotificationPulse();
950 synchronized (mInCallToneGeneratorLock) {
952 if (mInCallToneGenerator == null) {
953 int relativeToneVolume = getContext().getResources().getInteger(
954 R.integer.config_inCallNotificationVolumeRelative);
955 if (relativeToneVolume < ToneGenerator.MIN_VOLUME
956 || relativeToneVolume > ToneGenerator.MAX_VOLUME) {
957 relativeToneVolume = ToneGenerator.MAX_VOLUME;
960 mInCallToneGenerator = new ToneGenerator(
961 AudioManager.STREAM_VOICE_CALL, relativeToneVolume);
962 } catch (RuntimeException e) {
963 Log.e(TAG, "Error creating local tone generator: " + e);
964 mInCallToneGenerator = null;
968 if (mInCallToneGenerator != null) {
969 mInCallToneGenerator.release();
970 mInCallToneGenerator = null;
974 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
975 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
976 if (userHandle >= 0) {
977 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
978 REASON_USER_STOPPED, null);
980 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
981 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
982 if (userHandle >= 0) {
983 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
984 REASON_PROFILE_TURNED_OFF, null);
986 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
987 // turn off LED when user passes through lock screen
988 mNotificationLight.turnOff();
989 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
990 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
991 // reload per-user settings
992 mSettingsObserver.update(null);
993 mUserProfiles.updateCache(context);
994 // Refresh managed services
995 mConditionProviders.onUserSwitched(user);
996 mListeners.onUserSwitched(user);
997 mAssistants.onUserSwitched(user);
998 mZenModeHelper.onUserSwitched(user);
999 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
1000 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
1001 if (userId != USER_NULL) {
1002 mUserProfiles.updateCache(context);
1003 readDefaultApprovedServices(userId);
1005 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
1006 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
1007 mZenModeHelper.onUserRemoved(user);
1008 mRankingHelper.onUserRemoved(user);
1010 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
1011 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
1012 mConditionProviders.onUserUnlocked(user);
1013 mListeners.onUserUnlocked(user);
1014 mAssistants.onUserUnlocked(user);
1015 mZenModeHelper.onUserUnlocked(user);
1020 private final class SettingsObserver extends ContentObserver {
1021 private final Uri NOTIFICATION_BADGING_URI
1022 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
1023 private final Uri NOTIFICATION_LIGHT_PULSE_URI
1024 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1025 private final Uri NOTIFICATION_RATE_LIMIT_URI
1026 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
1028 SettingsObserver(Handler handler) {
1033 ContentResolver resolver = getContext().getContentResolver();
1034 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
1035 false, this, UserHandle.USER_ALL);
1036 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1037 false, this, UserHandle.USER_ALL);
1038 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
1039 false, this, UserHandle.USER_ALL);
1043 @Override public void onChange(boolean selfChange, Uri uri) {
1047 public void update(Uri uri) {
1048 ContentResolver resolver = getContext().getContentResolver();
1049 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1050 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
1051 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
1052 if (mNotificationPulseEnabled != pulseEnabled) {
1053 mNotificationPulseEnabled = pulseEnabled;
1054 updateNotificationPulse();
1057 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
1058 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
1059 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
1061 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
1062 mRankingHelper.updateBadgingEnabled();
1067 private SettingsObserver mSettingsObserver;
1068 protected ZenModeHelper mZenModeHelper;
1070 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1071 int[] ar = r.getIntArray(resid);
1075 final int len = ar.length > maxlen ? maxlen : ar.length;
1076 long[] out = new long[len];
1077 for (int i=0; i<len; i++) {
1083 public NotificationManagerService(Context context) {
1085 Notification.processWhitelistToken = WHITELIST_TOKEN;
1088 // TODO - replace these methods with a single VisibleForTesting constructor
1090 void setAudioManager(AudioManager audioMananger) {
1091 mAudioManager = audioMananger;
1095 void setVibrator(Vibrator vibrator) {
1096 mVibrator = vibrator;
1100 void setLights(Light light) {
1101 mNotificationLight = light;
1102 mAttentionLight = light;
1103 mNotificationPulseEnabled = true;
1107 void setScreenOn(boolean on) {
1112 int getNotificationRecordCount() {
1113 synchronized (mNotificationLock) {
1114 int count = mNotificationList.size() + mNotificationsByKey.size()
1115 + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
1116 // subtract duplicates
1117 for (NotificationRecord posted : mNotificationList) {
1118 if (mNotificationsByKey.containsKey(posted.getKey())) {
1121 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
1130 void clearNotifications() {
1131 mEnqueuedNotifications.clear();
1132 mNotificationList.clear();
1133 mNotificationsByKey.clear();
1134 mSummaryByGroupKey.clear();
1138 void addNotification(NotificationRecord r) {
1139 mNotificationList.add(r);
1140 mNotificationsByKey.put(r.sbn.getKey(), r);
1141 if (r.sbn.isGroup()) {
1142 mSummaryByGroupKey.put(r.getGroupKey(), r);
1147 void addEnqueuedNotification(NotificationRecord r) {
1148 mEnqueuedNotifications.add(r);
1152 void setSystemReady(boolean systemReady) {
1153 mSystemReady = systemReady;
1157 void setHandler(WorkerHandler handler) {
1162 void setFallbackVibrationPattern(long[] vibrationPattern) {
1163 mFallbackVibrationPattern = vibrationPattern;
1167 void setPackageManager(IPackageManager packageManager) {
1168 mPackageManager = packageManager;
1172 void setRankingHelper(RankingHelper rankingHelper) {
1173 mRankingHelper = rankingHelper;
1177 void setRankingHandler(RankingHandler rankingHandler) {
1178 mRankingHandler = rankingHandler;
1182 void setIsTelevision(boolean isTelevision) {
1183 mIsTelevision = isTelevision;
1187 void setUsageStats(NotificationUsageStats us) {
1191 // TODO: All tests should use this init instead of the one-off setters above.
1193 void init(Looper looper, IPackageManager packageManager,
1194 PackageManager packageManagerClient,
1195 LightsManager lightsManager, NotificationListeners notificationListeners,
1196 NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
1197 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1198 NotificationUsageStats usageStats, AtomicFile policyFile,
1199 ActivityManager activityManager, GroupHelper groupHelper) {
1200 Resources resources = getContext().getResources();
1201 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1202 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1203 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1205 mAm = ActivityManager.getService();
1206 mPackageManager = packageManager;
1207 mPackageManagerClient = packageManagerClient;
1208 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1209 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1210 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1211 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1212 mCompanionManager = companionManager;
1213 mActivityManager = activityManager;
1215 mHandler = new WorkerHandler(looper);
1216 mRankingThread.start();
1217 String[] extractorNames;
1219 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1220 } catch (Resources.NotFoundException e) {
1221 extractorNames = new String[0];
1223 mUsageStats = usageStats;
1224 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1225 mRankingHelper = new RankingHelper(getContext(),
1226 getContext().getPackageManager(),
1230 mConditionProviders = conditionProviders;
1231 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1232 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1234 public void onConfigChanged() {
1239 void onZenModeChanged() {
1240 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1241 getContext().sendBroadcastAsUser(
1242 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1243 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1244 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1245 synchronized (mNotificationLock) {
1246 updateInterruptionFilterLocked();
1251 void onPolicyChanged() {
1252 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1255 mSnoozeHelper = snoozeHelper;
1256 mGroupHelper = groupHelper;
1258 // This is a ManagedServices object that keeps track of the listeners.
1259 mListeners = notificationListeners;
1261 // This is a MangedServices object that keeps track of the assistant.
1262 mAssistants = notificationAssistants;
1264 mPolicyFile = policyFile;
1267 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1268 if (mStatusBar != null) {
1269 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1272 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1273 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1275 mFallbackVibrationPattern = getLongArray(resources,
1276 R.array.config_notificationFallbackVibePattern,
1277 VIBRATE_PATTERN_MAXLEN,
1278 DEFAULT_VIBRATE_PATTERN);
1280 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1282 // Don't start allowing notifications until the setup wizard has run once.
1283 // After that, including subsequent boots, init with notifications turned on.
1284 // This works on the first boot because the setup wizard will toggle this
1285 // flag at least once and we'll go back to 0 after that.
1286 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1287 Settings.Global.DEVICE_PROVISIONED, 0)) {
1288 mDisableNotificationEffects = true;
1290 mZenModeHelper.initZenMode();
1291 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1293 mUserProfiles.updateCache(getContext());
1294 listenForCallState();
1296 mSettingsObserver = new SettingsObserver(mHandler);
1298 mArchive = new Archive(resources.getInteger(
1299 R.integer.config_notificationServiceArchiveSize));
1301 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1302 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1306 public void onStart() {
1307 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1309 public void repost(int userId, NotificationRecord r) {
1312 Slog.d(TAG, "Reposting " + r.getKey());
1314 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1315 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1316 r.sbn.getNotification(), userId);
1317 } catch (Exception e) {
1318 Slog.e(TAG, "Cannot un-snooze notification", e);
1323 final File systemDir = new File(Environment.getDataDirectory(), "system");
1325 init(Looper.myLooper(),
1326 AppGlobals.getPackageManager(), getContext().getPackageManager(),
1327 getLocalService(LightsManager.class),
1328 new NotificationListeners(AppGlobals.getPackageManager()),
1329 new NotificationAssistants(AppGlobals.getPackageManager()),
1330 new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
1331 null, snoozeHelper, new NotificationUsageStats(getContext()),
1332 new AtomicFile(new File(systemDir, "notification_policy.xml")),
1333 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
1336 // register for various Intents
1337 IntentFilter filter = new IntentFilter();
1338 filter.addAction(Intent.ACTION_SCREEN_ON);
1339 filter.addAction(Intent.ACTION_SCREEN_OFF);
1340 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1341 filter.addAction(Intent.ACTION_USER_PRESENT);
1342 filter.addAction(Intent.ACTION_USER_STOPPED);
1343 filter.addAction(Intent.ACTION_USER_SWITCHED);
1344 filter.addAction(Intent.ACTION_USER_ADDED);
1345 filter.addAction(Intent.ACTION_USER_REMOVED);
1346 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1347 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1348 getContext().registerReceiver(mIntentReceiver, filter);
1350 IntentFilter pkgFilter = new IntentFilter();
1351 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1352 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1353 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1354 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1355 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1356 pkgFilter.addDataScheme("package");
1357 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1360 IntentFilter suspendedPkgFilter = new IntentFilter();
1361 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1362 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1363 suspendedPkgFilter, null, null);
1365 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1366 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1369 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1370 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1371 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1373 IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
1374 getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
1376 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
1377 getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
1379 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1380 publishLocalService(NotificationManagerInternal.class, mInternalService);
1383 private GroupHelper getGroupHelper() {
1384 return new GroupHelper(new GroupHelper.Callback() {
1386 public void addAutoGroup(String key) {
1387 synchronized (mNotificationLock) {
1388 addAutogroupKeyLocked(key);
1393 public void removeAutoGroup(String key) {
1394 synchronized (mNotificationLock) {
1395 removeAutogroupKeyLocked(key);
1400 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1401 createAutoGroupSummary(userId, pkg, triggeringKey);
1405 public void removeAutoGroupSummary(int userId, String pkg) {
1406 synchronized (mNotificationLock) {
1407 clearAutogroupSummaryLocked(userId, pkg);
1413 private void sendRegisteredOnlyBroadcast(String action) {
1414 getContext().sendBroadcastAsUser(new Intent(action)
1415 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1419 public void onBootPhase(int phase) {
1420 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1421 // no beeping until we're basically done booting
1422 mSystemReady = true;
1424 // Grab our optional AudioService
1425 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1426 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1427 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1428 mZenModeHelper.onSystemReady();
1429 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1430 // This observer will force an update when observe is called, causing us to
1431 // bind to listener services.
1432 mSettingsObserver.observe();
1433 mListeners.onBootPhaseAppsCanStart();
1434 mAssistants.onBootPhaseAppsCanStart();
1435 mConditionProviders.onBootPhaseAppsCanStart();
1439 @GuardedBy("mNotificationLock")
1440 private void updateListenerHintsLocked() {
1441 final int hints = calculateHints();
1442 if (hints == mListenerHints) return;
1443 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1444 mListenerHints = hints;
1445 scheduleListenerHintsChanged(hints);
1448 @GuardedBy("mNotificationLock")
1449 private void updateEffectsSuppressorLocked() {
1450 final long updatedSuppressedEffects = calculateSuppressedEffects();
1451 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1452 final List<ComponentName> suppressors = getSuppressors();
1453 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1454 mEffectsSuppressors = suppressors;
1455 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1456 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1459 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1460 boolean fromListener) {
1461 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1463 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1464 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1467 mRankingHelper.updateNotificationChannel(pkg, uid, channel);
1469 if (!fromListener) {
1470 final NotificationChannel modifiedChannel =
1471 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1472 mListeners.notifyNotificationChannelChanged(
1473 pkg, UserHandle.getUserHandleForUid(uid),
1474 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1480 private ArrayList<ComponentName> getSuppressors() {
1481 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1482 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1483 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1485 for (ManagedServiceInfo info : serviceInfoList) {
1486 names.add(info.component);
1493 private boolean removeDisabledHints(ManagedServiceInfo info) {
1494 return removeDisabledHints(info, 0);
1497 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1498 boolean removed = false;
1500 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1501 final int hint = mListenersDisablingEffects.keyAt(i);
1502 final ArraySet<ManagedServiceInfo> listeners =
1503 mListenersDisablingEffects.valueAt(i);
1505 if (hints == 0 || (hint & hints) == hint) {
1506 removed = removed || listeners.remove(info);
1513 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1514 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1515 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1518 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1519 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1522 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1523 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1527 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1528 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1529 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1532 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1533 hintListeners.add(info);
1536 private int calculateHints() {
1538 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1539 int hint = mListenersDisablingEffects.keyAt(i);
1540 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1542 if (!serviceInfoList.isEmpty()) {
1550 private long calculateSuppressedEffects() {
1551 int hints = calculateHints();
1552 long suppressedEffects = 0;
1554 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1555 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1558 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1559 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1562 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1563 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1566 return suppressedEffects;
1569 @GuardedBy("mNotificationLock")
1570 private void updateInterruptionFilterLocked() {
1571 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1572 if (interruptionFilter == mInterruptionFilter) return;
1573 mInterruptionFilter = interruptionFilter;
1574 scheduleInterruptionFilterChanged(interruptionFilter);
1578 INotificationManager getBinderService() {
1579 return INotificationManager.Stub.asInterface(mService);
1583 NotificationManagerInternal getInternalService() {
1584 return mInternalService;
1587 private final IBinder mService = new INotificationManager.Stub() {
1589 // ============================================================================
1592 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1595 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1596 + " duration=" + duration);
1599 if (pkg == null || callback == null) {
1600 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1603 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1604 final boolean isPackageSuspended =
1605 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1607 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1608 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1609 || isPackageSuspended)) {
1610 Slog.e(TAG, "Suppressing toast from package " + pkg
1611 + (isPackageSuspended
1612 ? " due to package suspended by administrator."
1613 : " by user request."));
1617 synchronized (mToastQueue) {
1618 int callingPid = Binder.getCallingPid();
1619 long callingId = Binder.clearCallingIdentity();
1622 int index = indexOfToastLocked(pkg, callback);
1623 // If it's already in the queue, we update it in place, we don't
1624 // move it to the end of the queue.
1626 record = mToastQueue.get(index);
1627 record.update(duration);
1629 // Limit the number of toasts that any given package except the android
1630 // package can enqueue. Prevents DOS attacks and deals with leaks.
1631 if (!isSystemToast) {
1633 final int N = mToastQueue.size();
1634 for (int i=0; i<N; i++) {
1635 final ToastRecord r = mToastQueue.get(i);
1636 if (r.pkg.equals(pkg)) {
1638 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1639 Slog.e(TAG, "Package has already posted " + count
1640 + " toasts. Not showing more. Package=" + pkg);
1647 Binder token = new Binder();
1648 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1649 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1650 mToastQueue.add(record);
1651 index = mToastQueue.size() - 1;
1652 keepProcessAliveIfNeededLocked(callingPid);
1654 // If it's at index 0, it's the current toast. It doesn't matter if it's
1655 // new or just been updated. Call back and tell it to show itself.
1656 // If the callback fails, this will remove it from the list, so don't
1657 // assume that it's valid after this.
1659 showNextToastLocked();
1662 Binder.restoreCallingIdentity(callingId);
1668 public void cancelToast(String pkg, ITransientNotification callback) {
1669 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1671 if (pkg == null || callback == null) {
1672 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1676 synchronized (mToastQueue) {
1677 long callingId = Binder.clearCallingIdentity();
1679 int index = indexOfToastLocked(pkg, callback);
1681 cancelToastLocked(index);
1683 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1684 + " callback=" + callback);
1687 Binder.restoreCallingIdentity(callingId);
1693 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1694 Notification notification, int userId) throws RemoteException {
1695 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1696 Binder.getCallingPid(), tag, id, notification, userId);
1700 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1701 checkCallerIsSystemOrSameApp(pkg);
1702 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1703 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1704 // Don't allow client applications to cancel foreground service notis or autobundled
1706 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1707 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1708 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1709 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1713 public void cancelAllNotifications(String pkg, int userId) {
1714 checkCallerIsSystemOrSameApp(pkg);
1716 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1717 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1719 // Calling from user space, don't allow the canceling of actively
1720 // running foreground services.
1721 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1722 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1723 REASON_APP_CANCEL_ALL, null);
1727 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1728 checkCallerIsSystem();
1730 mRankingHelper.setEnabled(pkg, uid, enabled);
1731 // Now, cancel any outstanding notifications that are part of a just-disabled app
1733 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1734 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1740 * Use this when you just want to know if notifications are OK for this package.
1743 public boolean areNotificationsEnabled(String pkg) {
1744 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1748 * Use this when you just want to know if notifications are OK for this package.
1751 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1752 checkCallerIsSystemOrSameApp(pkg);
1754 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1758 public int getPackageImportance(String pkg) {
1759 checkCallerIsSystemOrSameApp(pkg);
1760 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1764 public boolean canShowBadge(String pkg, int uid) {
1765 checkCallerIsSystem();
1766 return mRankingHelper.canShowBadge(pkg, uid);
1770 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1771 checkCallerIsSystem();
1772 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1777 public void createNotificationChannelGroups(String pkg,
1778 ParceledListSlice channelGroupList) throws RemoteException {
1779 checkCallerIsSystemOrSameApp(pkg);
1780 List<NotificationChannelGroup> groups = channelGroupList.getList();
1781 final int groupSize = groups.size();
1782 for (int i = 0; i < groupSize; i++) {
1783 final NotificationChannelGroup group = groups.get(i);
1784 Preconditions.checkNotNull(group, "group in list is null");
1785 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1786 true /* fromTargetApp */);
1787 mListeners.notifyNotificationChannelGroupChanged(pkg,
1788 UserHandle.of(UserHandle.getCallingUserId()), group,
1789 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1794 private void createNotificationChannelsImpl(String pkg, int uid,
1795 ParceledListSlice channelsList) {
1796 List<NotificationChannel> channels = channelsList.getList();
1797 final int channelsSize = channels.size();
1798 for (int i = 0; i < channelsSize; i++) {
1799 final NotificationChannel channel = channels.get(i);
1800 Preconditions.checkNotNull(channel, "channel in list is null");
1801 mRankingHelper.createNotificationChannel(pkg, uid, channel,
1802 true /* fromTargetApp */);
1803 mListeners.notifyNotificationChannelChanged(pkg,
1804 UserHandle.getUserHandleForUid(uid),
1805 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1806 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1812 public void createNotificationChannels(String pkg,
1813 ParceledListSlice channelsList) throws RemoteException {
1814 checkCallerIsSystemOrSameApp(pkg);
1815 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1819 public void createNotificationChannelsForPackage(String pkg, int uid,
1820 ParceledListSlice channelsList) throws RemoteException {
1821 checkCallerIsSystem();
1822 createNotificationChannelsImpl(pkg, uid, channelsList);
1826 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1827 checkCallerIsSystemOrSameApp(pkg);
1828 return mRankingHelper.getNotificationChannel(
1829 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1833 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1834 String channelId, boolean includeDeleted) {
1835 checkCallerIsSystem();
1836 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1840 public void deleteNotificationChannel(String pkg, String channelId) {
1841 checkCallerIsSystemOrSameApp(pkg);
1842 final int callingUid = Binder.getCallingUid();
1843 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1844 throw new IllegalArgumentException("Cannot delete default channel");
1846 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1847 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1848 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1849 mListeners.notifyNotificationChannelChanged(pkg,
1850 UserHandle.getUserHandleForUid(callingUid),
1851 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1852 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1857 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1859 checkCallerIsSystemOrSameApp(pkg);
1860 return new ParceledListSlice<>(new ArrayList(
1861 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1865 public void deleteNotificationChannelGroup(String pkg, String groupId) {
1866 checkCallerIsSystemOrSameApp(pkg);
1868 final int callingUid = Binder.getCallingUid();
1869 NotificationChannelGroup groupToDelete =
1870 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1871 if (groupToDelete != null) {
1872 List<NotificationChannel> deletedChannels =
1873 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1874 for (int i = 0; i < deletedChannels.size(); i++) {
1875 final NotificationChannel deletedChannel = deletedChannels.get(i);
1876 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1878 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1880 mListeners.notifyNotificationChannelChanged(pkg,
1881 UserHandle.getUserHandleForUid(callingUid),
1883 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1885 mListeners.notifyNotificationChannelGroupChanged(
1886 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1887 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1893 public void updateNotificationChannelForPackage(String pkg, int uid,
1894 NotificationChannel channel) {
1895 enforceSystemOrSystemUI("Caller not system or systemui");
1896 Preconditions.checkNotNull(channel);
1897 updateNotificationChannelInt(pkg, uid, channel, false);
1901 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1902 int uid, boolean includeDeleted) {
1903 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1904 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1908 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1909 boolean includeDeleted) {
1910 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1911 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1916 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1917 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1918 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1922 public int getDeletedChannelCount(String pkg, int uid) {
1923 enforceSystemOrSystemUI("getDeletedChannelCount");
1924 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1928 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1929 String pkg, int uid, boolean includeDeleted) {
1930 checkCallerIsSystem();
1931 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1935 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1936 String groupId, String pkg, int uid) {
1937 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1938 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1942 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1943 checkCallerIsSystemOrSameApp(pkg);
1944 return mRankingHelper.getNotificationChannels(
1945 pkg, Binder.getCallingUid(), false /* includeDeleted */);
1949 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1950 checkCallerIsSystem();
1952 // Cancel posted notifications
1953 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1954 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1956 final String[] packages = new String[] {packageName};
1957 final int[] uids = new int[] {uid};
1959 // Listener & assistant
1960 mListeners.onPackagesChanged(true, packages, uids);
1961 mAssistants.onPackagesChanged(true, packages, uids);
1964 mConditionProviders.onPackagesChanged(true, packages, uids);
1966 // Reset notification preferences
1968 mRankingHelper.onPackagesChanged(
1969 true, UserHandle.getCallingUserId(), packages, uids);
1977 * System-only API for getting a list of current (i.e. not cleared) notifications.
1979 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1980 * @returns A list of all the notifications, in natural order.
1983 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1984 // enforce() will ensure the calling uid has the correct permission
1985 getContext().enforceCallingOrSelfPermission(
1986 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1987 "NotificationManagerService.getActiveNotifications");
1989 StatusBarNotification[] tmp = null;
1990 int uid = Binder.getCallingUid();
1992 // noteOp will check to make sure the callingPkg matches the uid
1993 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1994 == AppOpsManager.MODE_ALLOWED) {
1995 synchronized (mNotificationLock) {
1996 tmp = new StatusBarNotification[mNotificationList.size()];
1997 final int N = mNotificationList.size();
1998 for (int i=0; i<N; i++) {
1999 tmp[i] = mNotificationList.get(i).sbn;
2007 * Public API for getting a list of current notifications for the calling package/uid.
2009 * Note that since notification posting is done asynchronously, this will not return
2010 * notifications that are in the process of being posted.
2012 * @returns A list of all the package's notifications, in natural order.
2015 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
2016 int incomingUserId) {
2017 checkCallerIsSystemOrSameApp(pkg);
2018 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2019 Binder.getCallingUid(), incomingUserId, true, false,
2020 "getAppActiveNotifications", pkg);
2021 synchronized (mNotificationLock) {
2022 final ArrayMap<String, StatusBarNotification> map
2023 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
2024 final int N = mNotificationList.size();
2025 for (int i = 0; i < N; i++) {
2026 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2027 mNotificationList.get(i).sbn);
2029 map.put(sbn.getKey(), sbn);
2032 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
2033 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
2035 map.put(sbn.getKey(), sbn);
2038 final int M = mEnqueuedNotifications.size();
2039 for (int i = 0; i < M; i++) {
2040 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2041 mEnqueuedNotifications.get(i).sbn);
2043 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
2046 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
2047 list.addAll(map.values());
2048 return new ParceledListSlice<StatusBarNotification>(list);
2052 private StatusBarNotification sanitizeSbn(String pkg, int userId,
2053 StatusBarNotification sbn) {
2054 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
2055 && (sbn.getNotification().flags
2056 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
2057 // We could pass back a cloneLight() but clients might get confused and
2058 // try to send this thing back to notify() again, which would not work
2060 return new StatusBarNotification(
2061 sbn.getPackageName(),
2063 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
2064 sbn.getNotification().clone(),
2065 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
2071 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
2073 * Requires ACCESS_NOTIFICATIONS which is signature|system.
2076 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
2077 // enforce() will ensure the calling uid has the correct permission
2078 getContext().enforceCallingOrSelfPermission(
2079 android.Manifest.permission.ACCESS_NOTIFICATIONS,
2080 "NotificationManagerService.getHistoricalNotifications");
2082 StatusBarNotification[] tmp = null;
2083 int uid = Binder.getCallingUid();
2085 // noteOp will check to make sure the callingPkg matches the uid
2086 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
2087 == AppOpsManager.MODE_ALLOWED) {
2088 synchronized (mArchive) {
2089 tmp = mArchive.getArray(count);
2096 * Register a listener binder directly with the notification manager.
2098 * Only works with system callers. Apps should extend
2099 * {@link android.service.notification.NotificationListenerService}.
2102 public void registerListener(final INotificationListener listener,
2103 final ComponentName component, final int userid) {
2104 enforceSystemOrSystemUI("INotificationManager.registerListener");
2105 mListeners.registerService(listener, component, userid);
2109 * Remove a listener binder directly
2112 public void unregisterListener(INotificationListener token, int userid) {
2113 mListeners.unregisterService(token, userid);
2117 * Allow an INotificationListener to simulate a "clear all" operation.
2119 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2121 * @param token The binder for the listener, to check that the caller is allowed
2124 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
2125 final int callingUid = Binder.getCallingUid();
2126 final int callingPid = Binder.getCallingPid();
2127 long identity = Binder.clearCallingIdentity();
2129 synchronized (mNotificationLock) {
2130 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2132 final int N = keys.length;
2133 for (int i = 0; i < N; i++) {
2134 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2135 if (r == null) continue;
2136 final int userId = r.sbn.getUserId();
2137 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2138 !mUserProfiles.isCurrentProfile(userId)) {
2139 throw new SecurityException("Disallowed call from listener: "
2142 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2143 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2147 cancelAllLocked(callingUid, callingPid, info.userid,
2148 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
2152 Binder.restoreCallingIdentity(identity);
2157 * Handle request from an approved listener to re-enable itself.
2159 * @param component The componenet to be re-enabled, caller must match package.
2162 public void requestBindListener(ComponentName component) {
2163 checkCallerIsSystemOrSameApp(component.getPackageName());
2164 long identity = Binder.clearCallingIdentity();
2166 ManagedServices manager =
2167 mAssistants.isComponentEnabledForCurrentProfiles(component)
2170 manager.setComponentState(component, true);
2172 Binder.restoreCallingIdentity(identity);
2177 public void requestUnbindListener(INotificationListener token) {
2178 long identity = Binder.clearCallingIdentity();
2180 // allow bound services to disable themselves
2181 synchronized (mNotificationLock) {
2182 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2183 info.getOwner().setComponentState(info.component, false);
2186 Binder.restoreCallingIdentity(identity);
2191 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2192 long identity = Binder.clearCallingIdentity();
2194 synchronized (mNotificationLock) {
2195 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2197 final int N = keys.length;
2198 for (int i = 0; i < N; i++) {
2199 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2200 if (r == null) continue;
2201 final int userId = r.sbn.getUserId();
2202 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2203 !mUserProfiles.isCurrentProfile(userId)) {
2204 throw new SecurityException("Disallowed call from listener: "
2208 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2209 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2210 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2212 UsageEvents.Event.USER_INTERACTION);
2219 Binder.restoreCallingIdentity(identity);
2224 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2226 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2228 * @param info The binder for the listener, to check that the caller is allowed
2230 @GuardedBy("mNotificationLock")
2231 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2232 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2233 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2234 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2236 userId, REASON_LISTENER_CANCEL, info);
2240 * Allow an INotificationListener to snooze a single notification until a context.
2242 * @param token The binder for the listener, to check that the caller is allowed
2245 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2246 String key, String snoozeCriterionId) {
2247 long identity = Binder.clearCallingIdentity();
2249 synchronized (mNotificationLock) {
2250 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2251 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2254 Binder.restoreCallingIdentity(identity);
2259 * Allow an INotificationListener to snooze a single notification until a time.
2261 * @param token The binder for the listener, to check that the caller is allowed
2264 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2266 long identity = Binder.clearCallingIdentity();
2268 synchronized (mNotificationLock) {
2269 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2270 snoozeNotificationInt(key, duration, null, info);
2273 Binder.restoreCallingIdentity(identity);
2278 * Allows the notification assistant to un-snooze a single notification.
2280 * @param token The binder for the assistant, to check that the caller is allowed
2283 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2284 long identity = Binder.clearCallingIdentity();
2286 synchronized (mNotificationLock) {
2287 final ManagedServiceInfo info =
2288 mAssistants.checkServiceTokenLocked(token);
2289 unsnoozeNotificationInt(key, info);
2292 Binder.restoreCallingIdentity(identity);
2297 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2299 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2301 * @param token The binder for the listener, to check that the caller is allowed
2304 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2305 String tag, int id) {
2306 final int callingUid = Binder.getCallingUid();
2307 final int callingPid = Binder.getCallingPid();
2308 long identity = Binder.clearCallingIdentity();
2310 synchronized (mNotificationLock) {
2311 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2312 if (info.supportsProfiles()) {
2313 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2314 + "from " + info.component
2315 + " use cancelNotification(key) instead.");
2317 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2318 pkg, tag, id, info.userid);
2322 Binder.restoreCallingIdentity(identity);
2327 * Allow an INotificationListener to request the list of outstanding notifications seen by
2328 * the current user. Useful when starting up, after which point the listener callbacks
2331 * @param token The binder for the listener, to check that the caller is allowed
2332 * @param keys An array of notification keys to fetch, or null to fetch everything
2333 * @returns The return value will contain the notifications specified in keys, in that
2334 * order, or if keys is null, all the notifications, in natural order.
2337 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2338 INotificationListener token, String[] keys, int trim) {
2339 synchronized (mNotificationLock) {
2340 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2341 final boolean getKeys = keys != null;
2342 final int N = getKeys ? keys.length : mNotificationList.size();
2343 final ArrayList<StatusBarNotification> list
2344 = new ArrayList<StatusBarNotification>(N);
2345 for (int i=0; i<N; i++) {
2346 final NotificationRecord r = getKeys
2347 ? mNotificationsByKey.get(keys[i])
2348 : mNotificationList.get(i);
2349 if (r == null) continue;
2350 StatusBarNotification sbn = r.sbn;
2351 if (!isVisibleToListener(sbn, info)) continue;
2352 StatusBarNotification sbnToSend =
2353 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2354 list.add(sbnToSend);
2356 return new ParceledListSlice<StatusBarNotification>(list);
2361 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2362 * seen by the current user. Useful when starting up, after which point the listener
2363 * callbacks should be used.
2365 * @param token The binder for the listener, to check that the caller is allowed
2366 * @returns The return value will contain the notifications specified in keys, in that
2367 * order, or if keys is null, all the notifications, in natural order.
2370 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2371 INotificationListener token, int trim) {
2372 synchronized (mNotificationLock) {
2373 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2374 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2375 final int N = snoozedRecords.size();
2376 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2377 for (int i=0; i < N; i++) {
2378 final NotificationRecord r = snoozedRecords.get(i);
2379 if (r == null) continue;
2380 StatusBarNotification sbn = r.sbn;
2381 if (!isVisibleToListener(sbn, info)) continue;
2382 StatusBarNotification sbnToSend =
2383 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2384 list.add(sbnToSend);
2386 return new ParceledListSlice<>(list);
2391 public void requestHintsFromListener(INotificationListener token, int hints) {
2392 final long identity = Binder.clearCallingIdentity();
2394 synchronized (mNotificationLock) {
2395 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2396 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2397 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2398 | HINT_HOST_DISABLE_CALL_EFFECTS;
2399 final boolean disableEffects = (hints & disableEffectsMask) != 0;
2400 if (disableEffects) {
2401 addDisabledHints(info, hints);
2403 removeDisabledHints(info, hints);
2405 updateListenerHintsLocked();
2406 updateEffectsSuppressorLocked();
2409 Binder.restoreCallingIdentity(identity);
2414 public int getHintsFromListener(INotificationListener token) {
2415 synchronized (mNotificationLock) {
2416 return mListenerHints;
2421 public void requestInterruptionFilterFromListener(INotificationListener token,
2422 int interruptionFilter) throws RemoteException {
2423 final long identity = Binder.clearCallingIdentity();
2425 synchronized (mNotificationLock) {
2426 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2427 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2428 updateInterruptionFilterLocked();
2431 Binder.restoreCallingIdentity(identity);
2436 public int getInterruptionFilterFromListener(INotificationListener token)
2437 throws RemoteException {
2438 synchronized (mNotificationLight) {
2439 return mInterruptionFilter;
2444 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2445 throws RemoteException {
2446 synchronized (mNotificationLock) {
2447 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2448 if (info == null) return;
2449 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2454 public int getZenMode() {
2455 return mZenModeHelper.getZenMode();
2459 public ZenModeConfig getZenModeConfig() {
2460 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2461 return mZenModeHelper.getConfig();
2465 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2466 enforceSystemOrSystemUI("INotificationManager.setZenMode");
2467 final long identity = Binder.clearCallingIdentity();
2469 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2471 Binder.restoreCallingIdentity(identity);
2476 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2477 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2478 return mZenModeHelper.getZenRules();
2482 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2483 Preconditions.checkNotNull(id, "Id is null");
2484 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2485 return mZenModeHelper.getAutomaticZenRule(id);
2489 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2490 throws RemoteException {
2491 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2492 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2493 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2494 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2495 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2497 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2498 "addAutomaticZenRule");
2502 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2503 throws RemoteException {
2504 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2505 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2506 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2507 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2508 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2510 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2511 "updateAutomaticZenRule");
2515 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2516 Preconditions.checkNotNull(id, "Id is null");
2517 // Verify that they can modify zen rules.
2518 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2520 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2524 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2525 Preconditions.checkNotNull(packageName, "Package name is null");
2526 enforceSystemOrSystemUI("removeAutomaticZenRules");
2528 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2532 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2533 Preconditions.checkNotNull(owner, "Owner is null");
2534 enforceSystemOrSystemUI("getRuleInstanceCount");
2536 return mZenModeHelper.getCurrentInstanceCount(owner);
2540 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2541 enforcePolicyAccess(pkg, "setInterruptionFilter");
2542 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2543 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2544 final long identity = Binder.clearCallingIdentity();
2546 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2548 Binder.restoreCallingIdentity(identity);
2553 public void notifyConditions(final String pkg, IConditionProvider provider,
2554 final Condition[] conditions) {
2555 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2556 checkCallerIsSystemOrSameApp(pkg);
2557 mHandler.post(new Runnable() {
2560 mConditionProviders.notifyConditions(pkg, info, conditions);
2566 public void requestUnbindProvider(IConditionProvider provider) {
2567 long identity = Binder.clearCallingIdentity();
2569 // allow bound services to disable themselves
2570 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2571 info.getOwner().setComponentState(info.component, false);
2573 Binder.restoreCallingIdentity(identity);
2578 public void requestBindProvider(ComponentName component) {
2579 checkCallerIsSystemOrSameApp(component.getPackageName());
2580 long identity = Binder.clearCallingIdentity();
2582 mConditionProviders.setComponentState(component, true);
2584 Binder.restoreCallingIdentity(identity);
2588 private void enforceSystemOrSystemUI(String message) {
2589 if (isCallerSystemOrPhone()) return;
2590 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2594 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2596 checkCallerIsSystemOrSameApp(pkg);
2597 } catch (SecurityException e) {
2598 getContext().enforceCallingPermission(
2599 android.Manifest.permission.STATUS_BAR_SERVICE,
2604 private void enforcePolicyAccess(int uid, String method) {
2605 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2606 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2609 boolean accessAllowed = false;
2610 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2611 final int packageCount = packages.length;
2612 for (int i = 0; i < packageCount; i++) {
2613 if (mConditionProviders.isPackageOrComponentAllowed(
2614 packages[i], UserHandle.getUserId(uid))) {
2615 accessAllowed = true;
2618 if (!accessAllowed) {
2619 Slog.w(TAG, "Notification policy access denied calling " + method);
2620 throw new SecurityException("Notification policy access denied");
2624 private void enforcePolicyAccess(String pkg, String method) {
2625 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2626 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2629 checkCallerIsSameApp(pkg);
2630 if (!checkPolicyAccess(pkg)) {
2631 Slog.w(TAG, "Notification policy access denied calling " + method);
2632 throw new SecurityException("Notification policy access denied");
2636 private boolean checkPackagePolicyAccess(String pkg) {
2637 return mConditionProviders.isPackageOrComponentAllowed(
2638 pkg, getCallingUserHandle().getIdentifier());
2641 private boolean checkPolicyAccess(String pkg) {
2643 int uid = getContext().getPackageManager().getPackageUidAsUser(
2644 pkg, UserHandle.getCallingUserId());
2645 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2646 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2650 } catch (NameNotFoundException e) {
2653 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2657 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2658 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2659 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2660 if (filter != null && filter.stats) {
2661 dumpJson(pw, filter);
2662 } else if (filter != null && filter.proto) {
2663 dumpProto(fd, filter);
2665 dumpImpl(pw, filter);
2670 public ComponentName getEffectsSuppressor() {
2671 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2675 public boolean matchesCallFilter(Bundle extras) {
2676 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2677 return mZenModeHelper.matchesCallFilter(
2678 Binder.getCallingUserHandle(),
2680 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2681 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2682 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2686 public boolean isSystemConditionProviderEnabled(String path) {
2687 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2688 return mConditionProviders.isSystemProviderEnabled(path);
2691 // Backup/restore interface
2693 public byte[] getBackupPayload(int user) {
2694 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2695 //TODO: http://b/22388012
2696 if (user != UserHandle.USER_SYSTEM) {
2697 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2700 synchronized(mPolicyFile) {
2701 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2703 writePolicyXml(baos, true /*forBackup*/);
2704 return baos.toByteArray();
2705 } catch (IOException e) {
2706 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2713 public void applyRestore(byte[] payload, int user) {
2714 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2715 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2716 if (payload == null) {
2717 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2720 //TODO: http://b/22388012
2721 if (user != UserHandle.USER_SYSTEM) {
2722 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2725 synchronized(mPolicyFile) {
2726 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2728 readPolicyXml(bais, true /*forRestore*/);
2730 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2731 Slog.w(TAG, "applyRestore: error reading payload", e);
2737 public boolean isNotificationPolicyAccessGranted(String pkg) {
2738 return checkPolicyAccess(pkg);
2742 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2743 enforceSystemOrSystemUIOrSamePackage(pkg,
2744 "request policy access status for another package");
2745 return checkPolicyAccess(pkg);
2749 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2750 throws RemoteException {
2751 checkCallerIsSystemOrShell();
2752 if (!mActivityManager.isLowRamDevice()) {
2753 mConditionProviders.setPackageOrComponentEnabled(
2754 pkg, getCallingUserHandle().getIdentifier(), true, granted);
2756 getContext().sendBroadcastAsUser(new Intent(
2757 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2759 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2760 getCallingUserHandle(), null);
2767 public Policy getNotificationPolicy(String pkg) {
2768 enforcePolicyAccess(pkg, "getNotificationPolicy");
2769 final long identity = Binder.clearCallingIdentity();
2771 return mZenModeHelper.getNotificationPolicy();
2773 Binder.restoreCallingIdentity(identity);
2778 public void setNotificationPolicy(String pkg, Policy policy) {
2779 enforcePolicyAccess(pkg, "setNotificationPolicy");
2780 final long identity = Binder.clearCallingIdentity();
2782 mZenModeHelper.setNotificationPolicy(policy);
2784 Binder.restoreCallingIdentity(identity);
2789 public List<String> getEnabledNotificationListenerPackages() {
2790 checkCallerIsSystem();
2791 return mListeners.getAllowedPackages(getCallingUserHandle().getIdentifier());
2795 public List<ComponentName> getEnabledNotificationListeners(int userId) {
2796 checkCallerIsSystem();
2797 return mListeners.getAllowedComponents(userId);
2801 public boolean isNotificationListenerAccessGranted(ComponentName listener) {
2802 Preconditions.checkNotNull(listener);
2803 checkCallerIsSystemOrSameApp(listener.getPackageName());
2804 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2805 getCallingUserHandle().getIdentifier());
2809 public boolean isNotificationListenerAccessGrantedForUser(ComponentName listener,
2811 Preconditions.checkNotNull(listener);
2812 checkCallerIsSystem();
2813 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2818 public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
2819 Preconditions.checkNotNull(assistant);
2820 checkCallerIsSystemOrSameApp(assistant.getPackageName());
2821 return mAssistants.isPackageOrComponentAllowed(assistant.flattenToString(),
2822 getCallingUserHandle().getIdentifier());
2826 public void setNotificationListenerAccessGranted(ComponentName listener,
2827 boolean granted) throws RemoteException {
2828 setNotificationListenerAccessGrantedForUser(
2829 listener, getCallingUserHandle().getIdentifier(), granted);
2833 public void setNotificationAssistantAccessGranted(ComponentName assistant,
2834 boolean granted) throws RemoteException {
2835 setNotificationAssistantAccessGrantedForUser(
2836 assistant, getCallingUserHandle().getIdentifier(), granted);
2840 public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
2841 boolean granted) throws RemoteException {
2842 Preconditions.checkNotNull(listener);
2843 checkCallerIsSystemOrShell();
2844 if (!mActivityManager.isLowRamDevice()) {
2845 mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
2846 userId, false, granted);
2847 mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
2848 userId, true, granted);
2850 getContext().sendBroadcastAsUser(new Intent(
2851 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2852 .setPackage(listener.getPackageName())
2853 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2854 getCallingUserHandle(), null);
2861 public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
2862 int userId, boolean granted) throws RemoteException {
2863 Preconditions.checkNotNull(assistant);
2864 checkCallerIsSystemOrShell();
2865 if (!mActivityManager.isLowRamDevice()) {
2866 mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
2867 userId, false, granted);
2868 mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
2869 userId, true, granted);
2871 getContext().sendBroadcastAsUser(new Intent(
2872 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2873 .setPackage(assistant.getPackageName())
2874 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2875 getCallingUserHandle(), null);
2882 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2883 Adjustment adjustment) throws RemoteException {
2884 final long identity = Binder.clearCallingIdentity();
2886 synchronized (mNotificationLock) {
2887 mAssistants.checkServiceTokenLocked(token);
2888 int N = mEnqueuedNotifications.size();
2889 for (int i = 0; i < N; i++) {
2890 final NotificationRecord n = mEnqueuedNotifications.get(i);
2891 if (Objects.equals(adjustment.getKey(), n.getKey())
2892 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2893 applyAdjustment(n, adjustment);
2899 Binder.restoreCallingIdentity(identity);
2904 public void applyAdjustmentFromAssistant(INotificationListener token,
2905 Adjustment adjustment) throws RemoteException {
2906 final long identity = Binder.clearCallingIdentity();
2908 synchronized (mNotificationLock) {
2909 mAssistants.checkServiceTokenLocked(token);
2910 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2911 applyAdjustment(n, adjustment);
2913 mRankingHandler.requestSort();
2915 Binder.restoreCallingIdentity(identity);
2920 public void applyAdjustmentsFromAssistant(INotificationListener token,
2921 List<Adjustment> adjustments) throws RemoteException {
2923 final long identity = Binder.clearCallingIdentity();
2925 synchronized (mNotificationLock) {
2926 mAssistants.checkServiceTokenLocked(token);
2927 for (Adjustment adjustment : adjustments) {
2928 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2929 applyAdjustment(n, adjustment);
2932 mRankingHandler.requestSort();
2934 Binder.restoreCallingIdentity(identity);
2939 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2940 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2941 Preconditions.checkNotNull(channel);
2942 Preconditions.checkNotNull(pkg);
2943 Preconditions.checkNotNull(user);
2945 verifyPrivilegedListener(token, user);
2946 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2950 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2951 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2952 Preconditions.checkNotNull(pkg);
2953 Preconditions.checkNotNull(user);
2954 verifyPrivilegedListener(token, user);
2956 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2957 false /* includeDeleted */);
2961 public ParceledListSlice<NotificationChannelGroup>
2962 getNotificationChannelGroupsFromPrivilegedListener(
2963 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2964 Preconditions.checkNotNull(pkg);
2965 Preconditions.checkNotNull(user);
2966 verifyPrivilegedListener(token, user);
2968 List<NotificationChannelGroup> groups = new ArrayList<>();
2969 groups.addAll(mRankingHelper.getNotificationChannelGroups(
2970 pkg, getUidForPackageAndUser(pkg, user)));
2971 return new ParceledListSlice<>(groups);
2974 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
2975 ManagedServiceInfo info;
2976 synchronized (mNotificationLock) {
2977 info = mListeners.checkServiceTokenLocked(token);
2979 if (!hasCompanionDevice(info)) {
2980 throw new SecurityException(info + " does not have access");
2982 if (!info.enabledAndUserMatches(user.getIdentifier())) {
2983 throw new SecurityException(info + " does not have access");
2987 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2989 long identity = Binder.clearCallingIdentity();
2991 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2993 Binder.restoreCallingIdentity(identity);
2999 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
3000 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
3001 throws RemoteException {
3002 new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
3006 private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
3010 if (adjustment.getSignals() != null) {
3011 Bundle.setDefusable(adjustment.getSignals(), true);
3012 r.addAdjustment(adjustment);
3016 @GuardedBy("mNotificationLock")
3017 void addAutogroupKeyLocked(String key) {
3018 NotificationRecord r = mNotificationsByKey.get(key);
3022 if (r.sbn.getOverrideGroupKey() == null) {
3023 addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
3024 EventLogTags.writeNotificationAutogrouped(key);
3025 mRankingHandler.requestSort();
3029 @GuardedBy("mNotificationLock")
3030 void removeAutogroupKeyLocked(String key) {
3031 NotificationRecord r = mNotificationsByKey.get(key);
3035 if (r.sbn.getOverrideGroupKey() != null) {
3036 addAutoGroupAdjustment(r, null);
3037 EventLogTags.writeNotificationUnautogrouped(key);
3038 mRankingHandler.requestSort();
3042 private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
3043 Bundle signals = new Bundle();
3044 signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
3045 Adjustment adjustment =
3046 new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
3047 r.addAdjustment(adjustment);
3050 // Clears the 'fake' auto-group summary.
3051 @GuardedBy("mNotificationLock")
3052 private void clearAutogroupSummaryLocked(int userId, String pkg) {
3053 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3054 if (summaries != null && summaries.containsKey(pkg)) {
3056 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
3057 if (removed != null) {
3058 boolean wasPosted = removeFromNotificationListsLocked(removed);
3059 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted, null);
3064 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
3065 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
3066 NotificationRecord summaryRecord = null;
3067 synchronized (mNotificationLock) {
3068 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
3069 if (notificationRecord == null) {
3070 // The notification could have been cancelled again already. A successive
3071 // adjustment will post a summary if needed.
3074 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
3075 userId = adjustedSbn.getUser().getIdentifier();
3076 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3077 if (summaries == null) {
3078 summaries = new ArrayMap<>();
3080 mAutobundledSummaries.put(userId, summaries);
3081 if (!summaries.containsKey(pkg)) {
3083 final ApplicationInfo appInfo =
3084 adjustedSbn.getNotification().extras.getParcelable(
3085 Notification.EXTRA_BUILDER_APPLICATION_INFO);
3086 final Bundle extras = new Bundle();
3087 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
3088 final String channelId = notificationRecord.getChannel().getId();
3089 final Notification summaryNotification =
3090 new Notification.Builder(getContext(), channelId)
3091 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
3092 .setGroupSummary(true)
3093 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
3094 .setGroup(GroupHelper.AUTOGROUP_KEY)
3095 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
3096 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
3097 .setColor(adjustedSbn.getNotification().color)
3100 summaryNotification.extras.putAll(extras);
3101 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
3102 if (appIntent != null) {
3103 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
3104 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
3106 final StatusBarNotification summarySbn =
3107 new StatusBarNotification(adjustedSbn.getPackageName(),
3108 adjustedSbn.getOpPkg(),
3110 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
3111 adjustedSbn.getInitialPid(), summaryNotification,
3112 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
3113 System.currentTimeMillis());
3114 summaryRecord = new NotificationRecord(getContext(), summarySbn,
3115 notificationRecord.getChannel());
3116 summaries.put(pkg, summarySbn.getKey());
3119 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
3120 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
3121 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
3125 private String disableNotificationEffects(NotificationRecord record) {
3126 if (mDisableNotificationEffects) {
3127 return "booleanState";
3129 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
3130 return "listenerHints";
3132 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
3138 private void dumpJson(PrintWriter pw, DumpFilter filter) {
3139 JSONObject dump = new JSONObject();
3141 dump.put("service", "Notification Manager");
3142 dump.put("bans", mRankingHelper.dumpBansJson(filter));
3143 dump.put("ranking", mRankingHelper.dumpJson(filter));
3144 dump.put("stats", mUsageStats.dumpJson(filter));
3145 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
3146 } catch (JSONException e) {
3147 e.printStackTrace();
3152 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
3153 final ProtoOutputStream proto = new ProtoOutputStream(fd);
3154 synchronized (mNotificationLock) {
3155 long records = proto.start(NotificationServiceDumpProto.RECORDS);
3156 int N = mNotificationList.size();
3158 for (int i = 0; i < N; i++) {
3159 final NotificationRecord nr = mNotificationList.get(i);
3160 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3161 nr.dump(proto, filter.redact);
3162 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
3165 N = mEnqueuedNotifications.size();
3167 for (int i = 0; i < N; i++) {
3168 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3169 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3170 nr.dump(proto, filter.redact);
3171 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
3174 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
3177 for (int i = 0; i < N; i++) {
3178 final NotificationRecord nr = snoozed.get(i);
3179 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3180 nr.dump(proto, filter.redact);
3181 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
3187 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
3188 mZenModeHelper.dump(proto);
3189 for (ComponentName suppressor : mEffectsSuppressors) {
3190 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
3197 void dumpImpl(PrintWriter pw, DumpFilter filter) {
3198 pw.print("Current Notification Manager state");
3199 if (filter.filtered) {
3200 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
3204 final boolean zenOnly = filter.filtered && filter.zen;
3207 synchronized (mToastQueue) {
3208 N = mToastQueue.size();
3210 pw.println(" Toast Queue:");
3211 for (int i=0; i<N; i++) {
3212 mToastQueue.get(i).dump(pw, " ", filter);
3219 synchronized (mNotificationLock) {
3221 N = mNotificationList.size();
3223 pw.println(" Notification List:");
3224 for (int i=0; i<N; i++) {
3225 final NotificationRecord nr = mNotificationList.get(i);
3226 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3227 nr.dump(pw, " ", getContext(), filter.redact);
3232 if (!filter.filtered) {
3235 pw.println(" Lights List:");
3236 for (int i=0; i<N; i++) {
3242 pw.println(mLights.get(i));
3246 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3247 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
3248 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3249 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
3250 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
3251 pw.println(" mCallState=" + callStateToString(mCallState));
3252 pw.println(" mSystemReady=" + mSystemReady);
3253 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
3255 pw.println(" mArchive=" + mArchive.toString());
3256 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
3258 while (iter.hasNext()) {
3259 final StatusBarNotification sbn = iter.next();
3260 if (filter != null && !filter.matches(sbn)) continue;
3261 pw.println(" " + sbn);
3263 if (iter.hasNext()) pw.println(" ...");
3269 N = mEnqueuedNotifications.size();
3271 pw.println(" Enqueued Notification List:");
3272 for (int i = 0; i < N; i++) {
3273 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3274 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3275 nr.dump(pw, " ", getContext(), filter.redact);
3280 mSnoozeHelper.dump(pw, filter);
3285 pw.println("\n Ranking Config:");
3286 mRankingHelper.dump(pw, " ", filter);
3288 pw.println("\n Notification listeners:");
3289 mListeners.dump(pw, filter);
3290 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3291 pw.print(" mListenersDisablingEffects: (");
3292 N = mListenersDisablingEffects.size();
3293 for (int i = 0; i < N; i++) {
3294 final int hint = mListenersDisablingEffects.keyAt(i);
3295 if (i > 0) pw.print(';');
3296 pw.print("hint[" + hint + "]:");
3298 final ArraySet<ManagedServiceInfo> listeners =
3299 mListenersDisablingEffects.valueAt(i);
3300 final int listenerSize = listeners.size();
3302 for (int j = 0; j < listenerSize; j++) {
3303 if (i > 0) pw.print(',');
3304 final ManagedServiceInfo listener = listeners.valueAt(i);
3305 pw.print(listener.component);
3309 pw.println("\n Notification assistant services:");
3310 mAssistants.dump(pw, filter);
3313 if (!filter.filtered || zenOnly) {
3314 pw.println("\n Zen Mode:");
3315 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3316 mZenModeHelper.dump(pw, " ");
3318 pw.println("\n Zen Log:");
3319 ZenLog.dump(pw, " ");
3322 pw.println("\n Condition providers:");
3323 mConditionProviders.dump(pw, filter);
3325 pw.println("\n Group summaries:");
3326 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3327 NotificationRecord r = entry.getValue();
3328 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3329 if (mNotificationsByKey.get(r.getKey()) != r) {
3330 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3331 r.dump(pw, " ", getContext(), filter.redact);
3336 pw.println("\n Usage Stats:");
3337 mUsageStats.dump(pw, " ", filter);
3343 * The private API only accessible to the system process.
3345 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3347 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3348 String tag, int id, Notification notification, int userId) {
3349 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3354 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3356 checkCallerIsSystem();
3357 mHandler.post(new Runnable() {
3360 synchronized (mNotificationLock) {
3361 removeForegroundServiceFlagByListLocked(
3362 mEnqueuedNotifications, pkg, notificationId, userId);
3363 removeForegroundServiceFlagByListLocked(
3364 mNotificationList, pkg, notificationId, userId);
3370 @GuardedBy("mNotificationLock")
3371 private void removeForegroundServiceFlagByListLocked(
3372 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3374 NotificationRecord r = findNotificationByListLocked(
3375 notificationList, pkg, null, notificationId, userId);
3379 StatusBarNotification sbn = r.sbn;
3380 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3381 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3382 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3383 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3384 sbn.getNotification().flags =
3385 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3386 mRankingHelper.sort(mNotificationList);
3387 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3391 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3392 final int callingPid, final String tag, final int id, final Notification notification,
3393 int incomingUserId) {
3395 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3396 + " notification=" + notification);
3398 checkCallerIsSystemOrSameApp(pkg);
3400 final int userId = ActivityManager.handleIncomingUser(callingPid,
3401 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3402 final UserHandle user = new UserHandle(userId);
3404 if (pkg == null || notification == null) {
3405 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3406 + " id=" + id + " notification=" + notification);
3409 // The system can post notifications for any package, let us resolve that.
3410 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3412 // Fix the notification as best we can.
3414 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3415 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3416 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3417 Notification.addFieldsFromContext(ai, notification);
3419 int canColorize = mPackageManagerClient.checkPermission(
3420 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
3421 if (canColorize == PERMISSION_GRANTED) {
3422 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3424 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3427 } catch (NameNotFoundException e) {
3428 Slog.e(TAG, "Cannot create a context for sending app", e);
3432 mUsageStats.registerEnqueuedByApp(pkg);
3434 // setup local book-keeping
3435 String channelId = notification.getChannelId();
3436 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3437 channelId = (new Notification.TvExtender(notification)).getChannelId();
3439 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3440 notificationUid, channelId, false /* includeDeleted */);
3441 if (channel == null) {
3442 final String noChannelStr = "No Channel found for "
3444 + ", channelId=" + channelId
3447 + ", opPkg=" + opPkg
3448 + ", callingUid=" + callingUid
3449 + ", userId=" + userId
3450 + ", incomingUserId=" + incomingUserId
3451 + ", notificationUid=" + notificationUid
3452 + ", notification=" + notification;
3453 Log.e(TAG, noChannelStr);
3454 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3455 "Failed to post notification on channel \"" + channelId + "\"\n" +
3456 "See log for more details");
3460 final StatusBarNotification n = new StatusBarNotification(
3461 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3462 user, null, System.currentTimeMillis());
3463 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3465 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
3469 // Whitelist pending intents.
3470 if (notification.allPendingIntents != null) {
3471 final int intentCount = notification.allPendingIntents.size();
3472 if (intentCount > 0) {
3473 final ActivityManagerInternal am = LocalServices
3474 .getService(ActivityManagerInternal.class);
3475 final long duration = LocalServices.getService(
3476 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3477 for (int i = 0; i < intentCount; i++) {
3478 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3479 if (pendingIntent != null) {
3480 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3481 WHITELIST_TOKEN, duration);
3487 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3490 private void doChannelWarningToast(CharSequence toastText) {
3491 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3492 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3493 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3494 if (warningEnabled) {
3495 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3496 Toast.LENGTH_SHORT);
3501 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3502 // The system can post notifications on behalf of any package it wants
3503 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3505 return getContext().getPackageManager()
3506 .getPackageUidAsUser(opPackageName, userId);
3507 } catch (NameNotFoundException e) {
3515 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3519 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3520 NotificationRecord r) {
3521 final String pkg = r.sbn.getPackageName();
3522 final String dialerPackage =
3523 getContext().getSystemService(TelecomManager.class).getSystemDialerPackage();
3524 final boolean isSystemNotification =
3525 isUidSystemOrPhone(callingUid) || ("android".equals(pkg))
3526 || TextUtils.equals(pkg, dialerPackage);
3527 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3529 // Limit the number of notifications that any given package except the android
3530 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3531 if (!isSystemNotification && !isNotificationFromListener) {
3532 synchronized (mNotificationLock) {
3533 if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
3534 // Ephemeral apps have some special constraints for notifications.
3535 // They are not allowed to create new notifications however they are allowed to
3536 // update notifications created by the system (e.g. a foreground service
3538 throw new SecurityException("Instant app " + pkg
3539 + " cannot create notifications");
3542 // rate limit updates that aren't completed progress notifications
3543 if (mNotificationsByKey.get(r.sbn.getKey()) != null
3544 && !r.getNotification().hasCompletedProgress()) {
3546 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3547 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3548 mUsageStats.registerOverRateQuota(pkg);
3549 final long now = SystemClock.elapsedRealtime();
3550 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3551 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3552 + ". Shedding events. package=" + pkg);
3553 mLastOverRateLogTime = now;
3559 // limit the number of outstanding notificationrecords an app can have
3560 int count = getNotificationCountLocked(pkg, userId, id, tag);
3561 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3562 mUsageStats.registerOverCountQuota(pkg);
3563 Slog.e(TAG, "Package has already posted or enqueued " + count
3564 + " notifications. Not showing more. package=" + pkg);
3571 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3572 MetricsLogger.action(r.getLogMaker()
3573 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3574 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3576 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3578 mSnoozeHelper.update(userId, r);
3585 if (isBlocked(r, mUsageStats)) {
3592 protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
3593 String excludedTag) {
3595 final int N = mNotificationList.size();
3596 for (int i = 0; i < N; i++) {
3597 final NotificationRecord existing = mNotificationList.get(i);
3598 if (existing.sbn.getPackageName().equals(pkg)
3599 && existing.sbn.getUserId() == userId) {
3600 if (existing.sbn.getId() == excludedId
3601 && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
3607 final int M = mEnqueuedNotifications.size();
3608 for (int i = 0; i < M; i++) {
3609 final NotificationRecord existing = mEnqueuedNotifications.get(i);
3610 if (existing.sbn.getPackageName().equals(pkg)
3611 && existing.sbn.getUserId() == userId) {
3618 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3619 final String pkg = r.sbn.getPackageName();
3620 final int callingUid = r.sbn.getUid();
3622 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3623 if (isPackageSuspended) {
3624 Slog.e(TAG, "Suppressing notification from package due to package "
3625 + "suspended by administrator.");
3626 usageStats.registerSuspendedByAdmin(r);
3627 return isPackageSuspended;
3630 final boolean isBlocked =
3631 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
3632 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3634 Slog.e(TAG, "Suppressing notification from package by user request.");
3635 usageStats.registerBlocked(r);
3640 protected class SnoozeNotificationRunnable implements Runnable {
3641 private final String mKey;
3642 private final long mDuration;
3643 private final String mSnoozeCriterionId;
3645 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3647 mDuration = duration;
3648 mSnoozeCriterionId = snoozeCriterionId;
3653 synchronized (mNotificationLock) {
3654 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3661 @GuardedBy("mNotificationLock")
3662 void snoozeLocked(NotificationRecord r) {
3663 if (r.sbn.isGroup()) {
3664 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3665 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3666 if (r.getNotification().isGroupSummary()) {
3667 // snooze summary and all children
3668 for (int i = 0; i < groupNotifications.size(); i++) {
3669 snoozeNotificationLocked(groupNotifications.get(i));
3672 // if there is a valid summary for this group, and we are snoozing the only
3673 // child, also snooze the summary
3674 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3675 if (groupNotifications.size() != 2) {
3676 snoozeNotificationLocked(r);
3678 // snooze summary and the one child
3679 for (int i = 0; i < groupNotifications.size(); i++) {
3680 snoozeNotificationLocked(groupNotifications.get(i));
3684 snoozeNotificationLocked(r);
3688 // just snooze the one notification
3689 snoozeNotificationLocked(r);
3693 @GuardedBy("mNotificationLock")
3694 void snoozeNotificationLocked(NotificationRecord r) {
3695 MetricsLogger.action(r.getLogMaker()
3696 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3697 .setType(MetricsEvent.TYPE_CLOSE)
3698 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3699 mSnoozeCriterionId == null ? 0 : 1));
3700 boolean wasPosted = removeFromNotificationListsLocked(r);
3701 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
3702 updateLightsLocked();
3703 if (mSnoozeCriterionId != null) {
3704 mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3705 mSnoozeHelper.snooze(r);
3707 mSnoozeHelper.snooze(r, mDuration);
3713 protected class EnqueueNotificationRunnable implements Runnable {
3714 private final NotificationRecord r;
3715 private final int userId;
3717 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3718 this.userId = userId;
3724 synchronized (mNotificationLock) {
3725 mEnqueuedNotifications.add(r);
3726 scheduleTimeoutLocked(r);
3728 final StatusBarNotification n = r.sbn;
3729 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3730 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3732 // Retain ranking information from previous record
3733 r.copyRankingInformation(old);
3736 final int callingUid = n.getUid();
3737 final int callingPid = n.getInitialPid();
3738 final Notification notification = n.getNotification();
3739 final String pkg = n.getPackageName();
3740 final int id = n.getId();
3741 final String tag = n.getTag();
3743 // Handle grouped notifications and bail out early if we
3744 // can to avoid extracting signals.
3745 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3747 // if this is a group child, unsnooze parent summary
3748 if (n.isGroup() && notification.isGroupChild()) {
3749 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3752 // This conditional is a dirty hack to limit the logging done on
3753 // behalf of the download manager without affecting other apps.
3754 if (!pkg.equals("com.android.providers.downloads")
3755 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3756 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3758 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3760 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3761 pkg, id, tag, userId, notification.toString(),
3765 mRankingHelper.extractSignals(r);
3767 // tell the assistant service about the notification
3768 if (mAssistants.isEnabled()) {
3769 mAssistants.onNotificationEnqueued(r);
3770 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3771 DELAY_FOR_ASSISTANT_TIME);
3773 mHandler.post(new PostNotificationRunnable(r.getKey()));
3779 protected class PostNotificationRunnable implements Runnable {
3780 private final String key;
3782 PostNotificationRunnable(String key) {
3788 synchronized (mNotificationLock) {
3790 NotificationRecord r = null;
3791 int N = mEnqueuedNotifications.size();
3792 for (int i = 0; i < N; i++) {
3793 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3794 if (Objects.equals(key, enqueued.getKey())) {
3800 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3803 NotificationRecord old = mNotificationsByKey.get(key);
3804 final StatusBarNotification n = r.sbn;
3805 final Notification notification = n.getNotification();
3806 int index = indexOfNotificationLocked(n.getKey());
3808 mNotificationList.add(r);
3809 mUsageStats.registerPostedByApp(r);
3811 old = mNotificationList.get(index);
3812 mNotificationList.set(index, r);
3813 mUsageStats.registerUpdatedByApp(r, old);
3814 // Make sure we don't lose the foreground service state.
3815 notification.flags |=
3816 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3820 mNotificationsByKey.put(n.getKey(), r);
3822 // Ensure if this is a foreground service that the proper additional
3824 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3825 notification.flags |= Notification.FLAG_ONGOING_EVENT
3826 | Notification.FLAG_NO_CLEAR;
3829 applyZenModeLocked(r);
3830 mRankingHelper.sort(mNotificationList);
3832 if (notification.getSmallIcon() != null) {
3833 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3834 mListeners.notifyPostedLocked(n, oldSbn);
3835 if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
3836 mHandler.post(new Runnable() {
3839 mGroupHelper.onNotificationPosted(n);
3844 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3845 if (old != null && !old.isCanceled) {
3846 mListeners.notifyRemovedLocked(n,
3847 NotificationListenerService.REASON_ERROR);
3848 mHandler.post(new Runnable() {
3851 mGroupHelper.onNotificationRemoved(n);
3855 // ATTENTION: in a future release we will bail out here
3856 // so that we do not play sounds, show lights, etc. for invalid
3858 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3859 + n.getPackageName());
3862 buzzBeepBlinkLocked(r);
3864 int N = mEnqueuedNotifications.size();
3865 for (int i = 0; i < N; i++) {
3866 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3867 if (Objects.equals(key, enqueued.getKey())) {
3868 mEnqueuedNotifications.remove(i);
3878 * Ensures that grouped notification receive their special treatment.
3880 * <p>Cancels group children if the new notification causes a group to lose
3883 * <p>Updates mSummaryByGroupKey.</p>
3885 @GuardedBy("mNotificationLock")
3886 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3887 int callingUid, int callingPid) {
3888 StatusBarNotification sbn = r.sbn;
3889 Notification n = sbn.getNotification();
3890 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3891 // notifications without a group shouldn't be a summary, otherwise autobundling can
3893 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3896 String group = sbn.getGroupKey();
3897 boolean isSummary = n.isGroupSummary();
3899 Notification oldN = old != null ? old.sbn.getNotification() : null;
3900 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3901 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3904 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3905 if (removedSummary != old) {
3907 removedSummary != null ? removedSummary.getKey() : "<null>";
3908 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3909 ", removed=" + removedKey);
3913 mSummaryByGroupKey.put(group, r);
3916 // Clear out group children of the old notification if the update
3917 // causes the group summary to go away. This happens when the old
3918 // notification was a summary and the new one isn't, or when the old
3919 // notification was a summary and its group key changed.
3920 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3921 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
3927 @GuardedBy("mNotificationLock")
3928 void scheduleTimeoutLocked(NotificationRecord record) {
3929 if (record.getNotification().getTimeoutAfter() > 0) {
3930 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3931 REQUEST_CODE_TIMEOUT,
3932 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3933 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3934 .appendPath(record.getKey()).build())
3935 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3936 .putExtra(EXTRA_KEY, record.getKey()),
3937 PendingIntent.FLAG_UPDATE_CURRENT);
3938 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3939 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
3944 @GuardedBy("mNotificationLock")
3945 void buzzBeepBlinkLocked(NotificationRecord record) {
3946 boolean buzz = false;
3947 boolean beep = false;
3948 boolean blink = false;
3950 final Notification notification = record.sbn.getNotification();
3951 final String key = record.getKey();
3953 // Should this notification make noise, vibe, or use the LED?
3954 final boolean aboveThreshold =
3955 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
3957 // Remember if this notification already owns the notification channels.
3958 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3959 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
3960 // These are set inside the conditional if the notification is allowed to make noise.
3961 boolean hasValidVibrate = false;
3962 boolean hasValidSound = false;
3964 if (aboveThreshold && isNotificationForCurrentUser(record)) {
3965 // If the notification will appear in the status bar, it should send an accessibility
3967 if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
3968 sendAccessibilityEvent(notification, record.sbn.getPackageName());
3970 if (mSystemReady && mAudioManager != null) {
3971 Uri soundUri = record.getSound();
3972 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
3974 long[] vibration = record.getVibration();
3975 // Demote sound to vibration if vibration missing & phone in vibration mode.
3976 if (vibration == null
3978 && (mAudioManager.getRingerModeInternal()
3979 == AudioManager.RINGER_MODE_VIBRATE)) {
3980 vibration = mFallbackVibrationPattern;
3982 hasValidVibrate = vibration != null;
3984 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
3986 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
3987 if (DBG) Slog.v(TAG, "Interrupting!");
3988 if (hasValidSound) {
3989 mSoundNotificationKey = key;
3991 playInCallNotification();
3994 beep = playSound(record, soundUri);
3998 final boolean ringerModeSilent =
3999 mAudioManager.getRingerModeInternal()
4000 == AudioManager.RINGER_MODE_SILENT;
4001 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
4002 mVibrateNotificationKey = key;
4004 buzz = playVibration(record, vibration, hasValidSound);
4009 // If a notification is updated to remove the actively playing sound or vibrate,
4010 // cancel that feedback now
4011 if (wasBeep && !hasValidSound) {
4014 if (wasBuzz && !hasValidVibrate) {
4015 clearVibrateLocked();
4019 // release the light
4020 boolean wasShowLights = mLights.remove(key);
4021 if (record.getLight() != null && aboveThreshold
4022 && ((record.getSuppressedVisualEffects()
4023 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
4025 updateLightsLocked();
4026 if (mUseAttentionLight) {
4027 mAttentionLight.pulse();
4030 } else if (wasShowLights) {
4031 updateLightsLocked();
4033 if (buzz || beep || blink) {
4034 MetricsLogger.action(record.getLogMaker()
4035 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
4036 .setType(MetricsEvent.TYPE_OPEN)
4037 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
4038 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
4042 @GuardedBy("mNotificationLock")
4043 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
4044 // Suppressed because it's a silent update
4045 final Notification notification = record.getNotification();
4047 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
4051 // muted by listener
4052 final String disableEffects = disableNotificationEffects(record);
4053 if (disableEffects != null) {
4054 ZenLog.traceDisableEffects(record, disableEffects);
4058 // suppressed due to DND
4059 if (record.isIntercepted()) {
4063 // Suppressed because another notification in its group handles alerting
4064 if (record.sbn.isGroup()) {
4065 return notification.suppressAlertingDueToGrouping();
4068 // Suppressed for being too recently noisy
4069 final String pkg = record.sbn.getPackageName();
4070 if (mUsageStats.isAlertRateLimited(pkg)) {
4071 Slog.e(TAG, "Muting recently noisy " + record.getKey());
4078 private boolean playSound(final NotificationRecord record, Uri soundUri) {
4079 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4080 // do not play notifications if there is a user of exclusive audio focus
4081 // or the device is in vibrate mode
4082 if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
4083 != AudioManager.RINGER_MODE_VIBRATE) {
4084 final long identity = Binder.clearCallingIdentity();
4086 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4087 if (player != null) {
4088 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
4089 + " with attributes " + record.getAudioAttributes());
4090 player.playAsync(soundUri, record.sbn.getUser(), looping,
4091 record.getAudioAttributes());
4094 } catch (RemoteException e) {
4096 Binder.restoreCallingIdentity(identity);
4102 private boolean playVibration(final NotificationRecord record, long[] vibration,
4103 boolean delayVibForSound) {
4104 // Escalate privileges so we can use the vibrator even if the
4105 // notifying app does not have the VIBRATE permission.
4106 long identity = Binder.clearCallingIdentity();
4108 final VibrationEffect effect;
4110 final boolean insistent =
4111 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4112 effect = VibrationEffect.createWaveform(
4113 vibration, insistent ? 0 : -1 /*repeatIndex*/);
4114 } catch (IllegalArgumentException e) {
4115 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
4116 Arrays.toString(vibration));
4119 if (delayVibForSound) {
4121 // delay the vibration by the same amount as the notification sound
4122 final int waitMs = mAudioManager.getFocusRampTimeMs(
4123 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
4124 record.getAudioAttributes());
4125 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
4127 Thread.sleep(waitMs);
4128 } catch (InterruptedException e) { }
4129 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4130 effect, record.getAudioAttributes());
4133 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4134 effect, record.getAudioAttributes());
4138 Binder.restoreCallingIdentity(identity);
4142 private boolean isNotificationForCurrentUser(NotificationRecord record) {
4143 final int currentUser;
4144 final long token = Binder.clearCallingIdentity();
4146 currentUser = ActivityManager.getCurrentUser();
4148 Binder.restoreCallingIdentity(token);
4150 return (record.getUserId() == UserHandle.USER_ALL ||
4151 record.getUserId() == currentUser ||
4152 mUserProfiles.isCurrentProfile(record.getUserId()));
4155 private void playInCallNotification() {
4159 // If toneGenerator creation fails, just continue the call
4160 // without playing the notification sound.
4162 synchronized (mInCallToneGeneratorLock) {
4163 if (mInCallToneGenerator != null) {
4164 // limit this tone to 1 second; BEEP2 should in fact be much shorter
4165 mInCallToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2, 1000);
4168 } catch (RuntimeException e) {
4169 Log.w(TAG, "Exception from ToneGenerator: " + e);
4175 @GuardedBy("mToastQueue")
4176 void showNextToastLocked() {
4177 ToastRecord record = mToastQueue.get(0);
4178 while (record != null) {
4179 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
4181 record.callback.show(record.token);
4182 scheduleTimeoutLocked(record);
4184 } catch (RemoteException e) {
4185 Slog.w(TAG, "Object died trying to show notification " + record.callback
4186 + " in package " + record.pkg);
4187 // remove it from the list and let the process die
4188 int index = mToastQueue.indexOf(record);
4190 mToastQueue.remove(index);
4192 keepProcessAliveIfNeededLocked(record.pid);
4193 if (mToastQueue.size() > 0) {
4194 record = mToastQueue.get(0);
4202 @GuardedBy("mToastQueue")
4203 void cancelToastLocked(int index) {
4204 ToastRecord record = mToastQueue.get(index);
4206 record.callback.hide();
4207 } catch (RemoteException e) {
4208 Slog.w(TAG, "Object died trying to hide notification " + record.callback
4209 + " in package " + record.pkg);
4210 // don't worry about this, we're about to remove it from
4214 ToastRecord lastToast = mToastQueue.remove(index);
4215 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
4217 keepProcessAliveIfNeededLocked(record.pid);
4218 if (mToastQueue.size() > 0) {
4219 // Show the next one. If the callback fails, this will remove
4220 // it from the list, so don't assume that the list hasn't changed
4221 // after this point.
4222 showNextToastLocked();
4226 @GuardedBy("mToastQueue")
4227 private void scheduleTimeoutLocked(ToastRecord r)
4229 mHandler.removeCallbacksAndMessages(r);
4230 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
4231 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
4232 mHandler.sendMessageDelayed(m, delay);
4235 private void handleTimeout(ToastRecord record)
4237 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
4238 synchronized (mToastQueue) {
4239 int index = indexOfToastLocked(record.pkg, record.callback);
4241 cancelToastLocked(index);
4246 @GuardedBy("mToastQueue")
4247 int indexOfToastLocked(String pkg, ITransientNotification callback)
4249 IBinder cbak = callback.asBinder();
4250 ArrayList<ToastRecord> list = mToastQueue;
4251 int len = list.size();
4252 for (int i=0; i<len; i++) {
4253 ToastRecord r = list.get(i);
4254 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
4261 @GuardedBy("mToastQueue")
4262 void keepProcessAliveIfNeededLocked(int pid)
4264 int toastCount = 0; // toasts from this pid
4265 ArrayList<ToastRecord> list = mToastQueue;
4266 int N = list.size();
4267 for (int i=0; i<N; i++) {
4268 ToastRecord r = list.get(i);
4274 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
4275 } catch (RemoteException e) {
4276 // Shouldn't happen.
4280 private void handleRankingReconsideration(Message message) {
4281 if (!(message.obj instanceof RankingReconsideration)) return;
4282 RankingReconsideration recon = (RankingReconsideration) message.obj;
4285 synchronized (mNotificationLock) {
4286 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4287 if (record == null) {
4290 int indexBefore = findNotificationRecordIndexLocked(record);
4291 boolean interceptBefore = record.isIntercepted();
4292 float contactAffinityBefore = record.getContactAffinity();
4293 int visibilityBefore = record.getPackageVisibilityOverride();
4294 recon.applyChangesLocked(record);
4295 applyZenModeLocked(record);
4296 mRankingHelper.sort(mNotificationList);
4297 int indexAfter = findNotificationRecordIndexLocked(record);
4298 boolean interceptAfter = record.isIntercepted();
4299 float contactAffinityAfter = record.getContactAffinity();
4300 int visibilityAfter = record.getPackageVisibilityOverride();
4301 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4302 || visibilityBefore != visibilityAfter;
4303 if (interceptBefore && !interceptAfter
4304 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
4305 buzzBeepBlinkLocked(record);
4309 mHandler.scheduleSendRankingUpdate();
4313 void handleRankingSort() {
4314 if (mRankingHelper == null) return;
4315 synchronized (mNotificationLock) {
4316 final int N = mNotificationList.size();
4317 // Any field that can change via one of the extractors needs to be added here.
4318 ArrayList<String> orderBefore = new ArrayList<>(N);
4319 int[] visibilities = new int[N];
4320 boolean[] showBadges = new boolean[N];
4321 ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
4322 ArrayList<String> groupKeyBefore = new ArrayList<>(N);
4323 ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
4324 ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
4325 for (int i = 0; i < N; i++) {
4326 final NotificationRecord r = mNotificationList.get(i);
4327 orderBefore.add(r.getKey());
4328 visibilities[i] = r.getPackageVisibilityOverride();
4329 showBadges[i] = r.canShowBadge();
4330 channelBefore.add(r.getChannel());
4331 groupKeyBefore.add(r.getGroupKey());
4332 overridePeopleBefore.add(r.getPeopleOverride());
4333 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
4334 mRankingHelper.extractSignals(r);
4336 mRankingHelper.sort(mNotificationList);
4337 for (int i = 0; i < N; i++) {
4338 final NotificationRecord r = mNotificationList.get(i);
4339 if (!orderBefore.get(i).equals(r.getKey())
4340 || visibilities[i] != r.getPackageVisibilityOverride()
4341 || showBadges[i] != r.canShowBadge()
4342 || !Objects.equals(channelBefore.get(i), r.getChannel())
4343 || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
4344 || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
4345 || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
4346 mHandler.scheduleSendRankingUpdate();
4353 @GuardedBy("mNotificationLock")
4354 private void recordCallerLocked(NotificationRecord record) {
4355 if (mZenModeHelper.isCall(record)) {
4356 mZenModeHelper.recordCaller(record);
4360 // let zen mode evaluate this record
4361 @GuardedBy("mNotificationLock")
4362 private void applyZenModeLocked(NotificationRecord record) {
4363 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4364 if (record.isIntercepted()) {
4365 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4366 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4367 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4368 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4369 record.setSuppressedVisualEffects(suppressed);
4371 record.setSuppressedVisualEffects(0);
4375 @GuardedBy("mNotificationLock")
4376 private int findNotificationRecordIndexLocked(NotificationRecord target) {
4377 return mRankingHelper.indexOf(mNotificationList, target);
4380 private void handleSendRankingUpdate() {
4381 synchronized (mNotificationLock) {
4382 mListeners.notifyRankingUpdateLocked();
4386 private void scheduleListenerHintsChanged(int state) {
4387 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4388 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4391 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4392 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4393 mHandler.obtainMessage(
4394 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4395 listenerInterruptionFilter,
4399 private void handleListenerHintsChanged(int hints) {
4400 synchronized (mNotificationLock) {
4401 mListeners.notifyListenerHintsChangedLocked(hints);
4405 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4406 synchronized (mNotificationLock) {
4407 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4411 protected class WorkerHandler extends Handler
4413 public WorkerHandler(Looper looper) {
4418 public void handleMessage(Message msg)
4422 case MESSAGE_TIMEOUT:
4423 handleTimeout((ToastRecord)msg.obj);
4425 case MESSAGE_SAVE_POLICY_FILE:
4426 handleSavePolicyFile();
4428 case MESSAGE_SEND_RANKING_UPDATE:
4429 handleSendRankingUpdate();
4431 case MESSAGE_LISTENER_HINTS_CHANGED:
4432 handleListenerHintsChanged(msg.arg1);
4434 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4435 handleListenerInterruptionFilterChanged(msg.arg1);
4440 protected void scheduleSendRankingUpdate() {
4441 if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4442 Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
4449 private final class RankingHandlerWorker extends Handler implements RankingHandler
4451 public RankingHandlerWorker(Looper looper) {
4456 public void handleMessage(Message msg) {
4458 case MESSAGE_RECONSIDER_RANKING:
4459 handleRankingReconsideration(msg);
4461 case MESSAGE_RANKING_SORT:
4462 handleRankingSort();
4467 public void requestSort() {
4468 removeMessages(MESSAGE_RANKING_SORT);
4469 Message msg = Message.obtain();
4470 msg.what = MESSAGE_RANKING_SORT;
4474 public void requestReconsideration(RankingReconsideration recon) {
4475 Message m = Message.obtain(this,
4476 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4477 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4478 sendMessageDelayed(m, delay);
4483 // ============================================================================
4484 static int clamp(int x, int low, int high) {
4485 return (x < low) ? low : ((x > high) ? high : x);
4488 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4489 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
4490 if (!manager.isEnabled()) {
4494 AccessibilityEvent event =
4495 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4496 event.setPackageName(packageName);
4497 event.setClassName(Notification.class.getName());
4498 event.setParcelableData(notification);
4499 CharSequence tickerText = notification.tickerText;
4500 if (!TextUtils.isEmpty(tickerText)) {
4501 event.getText().add(tickerText);
4504 manager.sendAccessibilityEvent(event);
4508 * Removes all NotificationsRecords with the same key as the given notification record
4509 * from both lists. Do not call this method while iterating over either list.
4511 @GuardedBy("mNotificationLock")
4512 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4513 // Remove from both lists, either list could have a separate Record for what is
4514 // effectively the same notification.
4515 boolean wasPosted = false;
4516 NotificationRecord recordInList = null;
4517 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4519 mNotificationList.remove(recordInList);
4520 mNotificationsByKey.remove(recordInList.sbn.getKey());
4523 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4525 mEnqueuedNotifications.remove(recordInList);
4530 @GuardedBy("mNotificationLock")
4531 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4532 boolean wasPosted, String listenerName) {
4533 final String canceledKey = r.getKey();
4536 recordCallerLocked(r);
4540 if (r.getNotification().deleteIntent != null) {
4542 r.getNotification().deleteIntent.send();
4543 } catch (PendingIntent.CanceledException ex) {
4544 // do nothing - there's no relevant way to recover, and
4545 // no reason to let this propagate
4546 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4551 // Only cancel these if this notification actually got to be posted.
4554 if (r.getNotification().getSmallIcon() != null) {
4555 if (reason != REASON_SNOOZED) {
4556 r.isCanceled = true;
4558 mListeners.notifyRemovedLocked(r.sbn, reason);
4559 mHandler.post(new Runnable() {
4562 mGroupHelper.onNotificationRemoved(r.sbn);
4568 if (canceledKey.equals(mSoundNotificationKey)) {
4569 mSoundNotificationKey = null;
4570 final long identity = Binder.clearCallingIdentity();
4572 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4573 if (player != null) {
4576 } catch (RemoteException e) {
4578 Binder.restoreCallingIdentity(identity);
4583 if (canceledKey.equals(mVibrateNotificationKey)) {
4584 mVibrateNotificationKey = null;
4585 long identity = Binder.clearCallingIdentity();
4590 Binder.restoreCallingIdentity(identity);
4595 mLights.remove(canceledKey);
4598 // Record usage stats
4599 // TODO: add unbundling stats?
4602 case REASON_CANCEL_ALL:
4603 case REASON_LISTENER_CANCEL:
4604 case REASON_LISTENER_CANCEL_ALL:
4605 mUsageStats.registerDismissedByUser(r);
4607 case REASON_APP_CANCEL:
4608 case REASON_APP_CANCEL_ALL:
4609 mUsageStats.registerRemovedByApp(r);
4613 String groupKey = r.getGroupKey();
4614 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4615 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4616 mSummaryByGroupKey.remove(groupKey);
4618 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4619 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4620 summaries.remove(r.sbn.getPackageName());
4623 // Save it for users of getHistoricalNotifications()
4624 mArchive.record(r.sbn);
4626 final long now = System.currentTimeMillis();
4627 MetricsLogger.action(r.getLogMaker(now)
4628 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4629 .setType(MetricsEvent.TYPE_DISMISS)
4630 .setSubtype(reason));
4631 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4632 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
4636 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4637 * and none of the {@code mustNotHaveFlags}.
4639 void cancelNotification(final int callingUid, final int callingPid,
4640 final String pkg, final String tag, final int id,
4641 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4642 final int userId, final int reason, final ManagedServiceInfo listener) {
4643 // In enqueueNotificationInternal notifications are added by scheduling the
4644 // work on the worker handler. Hence, we also schedule the cancel on this
4645 // handler to avoid a scenario where an add notification call followed by a
4646 // remove notification call ends up in not removing the notification.
4647 mHandler.post(new Runnable() {
4650 String listenerName = listener == null ? null : listener.component.toShortString();
4651 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4652 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4654 synchronized (mNotificationLock) {
4655 // Look for the notification, searching both the posted and enqueued lists.
4656 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4658 // The notification was found, check if it should be removed.
4660 // Ideally we'd do this in the caller of this method. However, that would
4661 // require the caller to also find the notification.
4662 if (reason == REASON_CLICK) {
4663 mUsageStats.registerClickedByUser(r);
4666 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4669 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4673 // Cancel the notification.
4674 boolean wasPosted = removeFromNotificationListsLocked(r);
4675 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4676 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4678 updateLightsLocked();
4680 // No notification was found, assume that it is snoozed and cancel it.
4681 if (reason != REASON_SNOOZED) {
4682 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4694 * Determine whether the userId applies to the notification in question, either because
4695 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4697 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4699 // looking for USER_ALL notifications? match everything
4700 userId == UserHandle.USER_ALL
4701 // a notification sent to USER_ALL matches any query
4702 || r.getUserId() == UserHandle.USER_ALL
4703 // an exact user match
4704 || r.getUserId() == userId;
4708 * Determine whether the userId applies to the notification in question, either because
4709 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4710 * because it matches one of the users profiles.
4712 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4713 return notificationMatchesUserId(r, userId)
4714 || mUserProfiles.isCurrentProfile(r.getUserId());
4718 * Cancels all notifications from a given package that have all of the
4719 * {@code mustHaveFlags}.
4721 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4722 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4723 ManagedServiceInfo listener) {
4724 mHandler.post(new Runnable() {
4727 String listenerName = listener == null ? null : listener.component.toShortString();
4728 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4729 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4732 // Why does this parameter exist? Do we actually want to execute the above if doit
4738 synchronized (mNotificationLock) {
4739 FlagChecker flagChecker = (int flags) -> {
4740 if ((flags & mustHaveFlags) != mustHaveFlags) {
4743 if ((flags & mustNotHaveFlags) != 0) {
4748 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4749 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4750 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4751 listenerName, true /* wasPosted */);
4752 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4753 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4754 flagChecker, false /*includeCurrentProfiles*/, userId,
4755 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4756 mSnoozeHelper.cancel(userId, pkg);
4762 private interface FlagChecker {
4763 // Returns false if these flags do not pass the defined flag test.
4764 public boolean apply(int flags);
4767 @GuardedBy("mNotificationLock")
4768 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4769 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4770 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4771 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4772 ArrayList<NotificationRecord> canceledNotifications = null;
4773 for (int i = notificationList.size() - 1; i >= 0; --i) {
4774 NotificationRecord r = notificationList.get(i);
4775 if (includeCurrentProfiles) {
4776 if (!notificationMatchesCurrentProfiles(r, userId)) {
4779 } else if (!notificationMatchesUserId(r, userId)) {
4782 // Don't remove notifications to all, if there's no package name specified
4783 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4786 if (!flagChecker.apply(r.getFlags())) {
4789 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4792 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4795 if (canceledNotifications == null) {
4796 canceledNotifications = new ArrayList<>();
4798 notificationList.remove(i);
4799 mNotificationsByKey.remove(r.getKey());
4800 canceledNotifications.add(r);
4801 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4803 if (canceledNotifications != null) {
4804 final int M = canceledNotifications.size();
4805 for (int i = 0; i < M; i++) {
4806 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4807 listenerName, false /* sendDelete */, flagChecker);
4809 updateLightsLocked();
4813 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4814 ManagedServiceInfo listener) {
4815 String listenerName = listener == null ? null : listener.component.toShortString();
4816 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4821 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4822 snoozeCriterionId, listenerName));
4824 // Needs to post so that it can cancel notifications not yet enqueued.
4825 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4828 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4829 String listenerName = listener == null ? null : listener.component.toShortString();
4831 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4833 mSnoozeHelper.repost(key);
4837 @GuardedBy("mNotificationLock")
4838 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4839 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4840 mHandler.post(new Runnable() {
4843 synchronized (mNotificationLock) {
4844 String listenerName =
4845 listener == null ? null : listener.component.toShortString();
4846 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4847 null, userId, 0, 0, reason, listenerName);
4849 FlagChecker flagChecker = (int flags) -> {
4850 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4857 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4858 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4859 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4860 listenerName, true);
4861 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4862 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4863 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4864 reason, listenerName, false);
4865 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4871 // Warning: The caller is responsible for invoking updateLightsLocked().
4872 @GuardedBy("mNotificationLock")
4873 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4874 String listenerName, boolean sendDelete, FlagChecker flagChecker) {
4875 Notification n = r.getNotification();
4876 if (!n.isGroupSummary()) {
4880 String pkg = r.sbn.getPackageName();
4883 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4887 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4888 sendDelete, true, flagChecker);
4889 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4890 listenerName, sendDelete, false, flagChecker);
4893 @GuardedBy("mNotificationLock")
4894 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4895 NotificationRecord parentNotification, int callingUid, int callingPid,
4896 String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
4897 final String pkg = parentNotification.sbn.getPackageName();
4898 final int userId = parentNotification.getUserId();
4899 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4900 for (int i = notificationList.size() - 1; i >= 0; i--) {
4901 final NotificationRecord childR = notificationList.get(i);
4902 final StatusBarNotification childSbn = childR.sbn;
4903 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4904 childR.getGroupKey().equals(parentNotification.getGroupKey())
4905 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
4906 && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
4907 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4908 childSbn.getTag(), userId, 0, 0, reason, listenerName);
4909 notificationList.remove(i);
4910 mNotificationsByKey.remove(childR.getKey());
4911 cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName);
4916 @GuardedBy("mNotificationLock")
4917 void updateLightsLocked()
4919 // handle notification lights
4920 NotificationRecord ledNotification = null;
4921 while (ledNotification == null && !mLights.isEmpty()) {
4922 final String owner = mLights.get(mLights.size() - 1);
4923 ledNotification = mNotificationsByKey.get(owner);
4924 if (ledNotification == null) {
4925 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4926 mLights.remove(owner);
4930 // Don't flash while we are in a call or screen is on
4931 if (ledNotification == null || mInCall || mScreenOn) {
4932 mNotificationLight.turnOff();
4934 NotificationRecord.Light light = ledNotification.getLight();
4935 if (light != null && mNotificationPulseEnabled) {
4937 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4938 light.onMs, light.offMs);
4943 @GuardedBy("mNotificationLock")
4944 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4945 String groupKey, int userId) {
4946 List<NotificationRecord> records = new ArrayList<>();
4947 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4949 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4954 @GuardedBy("mNotificationLock")
4955 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4956 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4957 List<NotificationRecord> records = new ArrayList<>();
4958 final int len = list.size();
4959 for (int i = 0; i < len; i++) {
4960 NotificationRecord r = list.get(i);
4961 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4962 && r.sbn.getPackageName().equals(pkg)) {
4969 // Searches both enqueued and posted notifications by key.
4970 // TODO: need to combine a bunch of these getters with slightly different behavior.
4971 // TODO: Should enqueuing just add to mNotificationsByKey instead?
4972 @GuardedBy("mNotificationLock")
4973 private NotificationRecord findNotificationByKeyLocked(String key) {
4974 NotificationRecord r;
4975 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4978 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4984 @GuardedBy("mNotificationLock")
4985 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
4986 NotificationRecord r;
4987 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4990 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4997 @GuardedBy("mNotificationLock")
4998 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4999 String pkg, String tag, int id, int userId) {
5000 final int len = list.size();
5001 for (int i = 0; i < len; i++) {
5002 NotificationRecord r = list.get(i);
5003 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
5004 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
5011 @GuardedBy("mNotificationLock")
5012 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5014 final int N = list.size();
5015 for (int i = 0; i < N; i++) {
5016 if (key.equals(list.get(i).getKey())) {
5023 @GuardedBy("mNotificationLock")
5024 int indexOfNotificationLocked(String key) {
5025 final int N = mNotificationList.size();
5026 for (int i = 0; i < N; i++) {
5027 if (key.equals(mNotificationList.get(i).getKey())) {
5034 private void updateNotificationPulse() {
5035 synchronized (mNotificationLock) {
5036 updateLightsLocked();
5040 protected boolean isCallingUidSystem() {
5041 final int uid = Binder.getCallingUid();
5042 return uid == Process.SYSTEM_UID;
5045 protected boolean isUidSystemOrPhone(int uid) {
5046 final int appid = UserHandle.getAppId(uid);
5047 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
5050 // TODO: Most calls should probably move to isCallerSystem.
5051 protected boolean isCallerSystemOrPhone() {
5052 return isUidSystemOrPhone(Binder.getCallingUid());
5055 private void checkCallerIsSystemOrShell() {
5056 if (Binder.getCallingUid() == Process.SHELL_UID) {
5059 checkCallerIsSystem();
5062 private void checkCallerIsSystem() {
5063 if (isCallerSystemOrPhone()) {
5066 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
5069 private void checkCallerIsSystemOrSameApp(String pkg) {
5070 if (isCallerSystemOrPhone()) {
5073 checkCallerIsSameApp(pkg);
5076 private boolean isCallerInstantApp(String pkg) {
5077 // System is always allowed to act for ephemeral apps.
5078 if (isCallerSystemOrPhone()) {
5082 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
5085 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
5086 UserHandle.getCallingUserId());
5088 throw new SecurityException("Unknown package " + pkg);
5090 return ai.isInstantApp();
5091 } catch (RemoteException re) {
5092 throw new SecurityException("Unknown package " + pkg, re);
5097 private void checkCallerIsSameApp(String pkg) {
5098 final int uid = Binder.getCallingUid();
5100 ApplicationInfo ai = mPackageManager.getApplicationInfo(
5101 pkg, 0, UserHandle.getCallingUserId());
5103 throw new SecurityException("Unknown package " + pkg);
5105 if (!UserHandle.isSameApp(ai.uid, uid)) {
5106 throw new SecurityException("Calling uid " + uid + " gave package "
5107 + pkg + " which is owned by uid " + ai.uid);
5109 } catch (RemoteException re) {
5110 throw new SecurityException("Unknown package " + pkg + "\n" + re);
5114 private static String callStateToString(int state) {
5116 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
5117 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
5118 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
5119 default: return "CALL_STATE_UNKNOWN_" + state;
5123 private void listenForCallState() {
5124 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
5126 public void onCallStateChanged(int state, String incomingNumber) {
5127 if (mCallState == state) return;
5128 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
5131 }, PhoneStateListener.LISTEN_CALL_STATE);
5135 * Generates a NotificationRankingUpdate from 'sbns', considering only
5136 * notifications visible to the given listener.
5138 @GuardedBy("mNotificationLock")
5139 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
5140 final int N = mNotificationList.size();
5141 ArrayList<String> keys = new ArrayList<String>(N);
5142 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
5143 ArrayList<Integer> importance = new ArrayList<>(N);
5144 Bundle overrideGroupKeys = new Bundle();
5145 Bundle visibilityOverrides = new Bundle();
5146 Bundle suppressedVisualEffects = new Bundle();
5147 Bundle explanation = new Bundle();
5148 Bundle channels = new Bundle();
5149 Bundle overridePeople = new Bundle();
5150 Bundle snoozeCriteria = new Bundle();
5151 Bundle showBadge = new Bundle();
5152 for (int i = 0; i < N; i++) {
5153 NotificationRecord record = mNotificationList.get(i);
5154 if (!isVisibleToListener(record.sbn, info)) {
5157 final String key = record.sbn.getKey();
5159 importance.add(record.getImportance());
5160 if (record.getImportanceExplanation() != null) {
5161 explanation.putCharSequence(key, record.getImportanceExplanation());
5163 if (record.isIntercepted()) {
5164 interceptedKeys.add(key);
5167 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
5168 if (record.getPackageVisibilityOverride()
5169 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
5170 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
5172 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
5173 channels.putParcelable(key, record.getChannel());
5174 overridePeople.putStringArrayList(key, record.getPeopleOverride());
5175 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
5176 showBadge.putBoolean(key, record.canShowBadge());
5178 final int M = keys.size();
5179 String[] keysAr = keys.toArray(new String[M]);
5180 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
5181 int[] importanceAr = new int[M];
5182 for (int i = 0; i < M; i++) {
5183 importanceAr[i] = importance.get(i);
5185 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
5186 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
5187 channels, overridePeople, snoozeCriteria, showBadge);
5190 boolean hasCompanionDevice(ManagedServiceInfo info) {
5191 if (mCompanionManager == null) {
5192 mCompanionManager = getCompanionManager();
5194 // Companion mgr doesn't exist on all device types
5195 if (mCompanionManager == null) {
5198 long identity = Binder.clearCallingIdentity();
5200 List<String> associations = mCompanionManager.getAssociations(
5201 info.component.getPackageName(), info.userid);
5202 if (!ArrayUtils.isEmpty(associations)) {
5205 } catch (SecurityException se) {
5206 // Not a privileged listener
5207 } catch (RemoteException re) {
5208 Slog.e(TAG, "Cannot reach companion device service", re);
5209 } catch (Exception e) {
5210 Slog.e(TAG, "Cannot verify listener " + info, e);
5212 Binder.restoreCallingIdentity(identity);
5217 protected ICompanionDeviceManager getCompanionManager() {
5218 return ICompanionDeviceManager.Stub.asInterface(
5219 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
5222 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5223 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5226 // TODO: remove this for older listeners.
5230 private boolean isPackageSuspendedForUser(String pkg, int uid) {
5231 int userId = UserHandle.getUserId(uid);
5233 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
5234 } catch (RemoteException re) {
5235 throw new SecurityException("Could not talk to package manager service");
5236 } catch (IllegalArgumentException ex) {
5237 // Package not found.
5242 private class TrimCache {
5243 StatusBarNotification heavy;
5244 StatusBarNotification sbnClone;
5245 StatusBarNotification sbnCloneLight;
5247 TrimCache(StatusBarNotification sbn) {
5251 StatusBarNotification ForListener(ManagedServiceInfo info) {
5252 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5253 if (sbnCloneLight == null) {
5254 sbnCloneLight = heavy.cloneLight();
5256 return sbnCloneLight;
5258 if (sbnClone == null) {
5259 sbnClone = heavy.clone();
5266 public class NotificationAssistants extends ManagedServices {
5267 static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
5269 public NotificationAssistants(IPackageManager pm) {
5270 super(getContext(), mNotificationLock, mUserProfiles, pm);
5274 protected Config getConfig() {
5275 Config c = new Config();
5276 c.caption = "notification assistant service";
5277 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5278 c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
5279 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5280 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5281 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5282 c.clientLabel = R.string.notification_ranker_binding_label;
5287 protected IInterface asInterface(IBinder binder) {
5288 return INotificationListener.Stub.asInterface(binder);
5292 protected boolean checkType(IInterface service) {
5293 return service instanceof INotificationListener;
5297 protected void onServiceAdded(ManagedServiceInfo info) {
5298 mListeners.registerGuestService(info);
5302 @GuardedBy("mNotificationLock")
5303 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5304 mListeners.unregisterService(removed.service, removed.userid);
5307 public void onNotificationEnqueued(final NotificationRecord r) {
5308 final StatusBarNotification sbn = r.sbn;
5309 TrimCache trimCache = new TrimCache(sbn);
5311 // There should be only one, but it's a list, so while we enforce
5312 // singularity elsewhere, we keep it general here, to avoid surprises.
5313 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5314 boolean sbnVisible = isVisibleToListener(sbn, info);
5319 final int importance = r.getImportance();
5320 final boolean fromUser = r.isImportanceFromUser();
5321 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5322 mHandler.post(new Runnable() {
5325 notifyEnqueued(info, sbnToPost);
5331 private void notifyEnqueued(final ManagedServiceInfo info,
5332 final StatusBarNotification sbn) {
5333 final INotificationListener assistant = (INotificationListener) info.service;
5334 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5336 assistant.onNotificationEnqueued(sbnHolder);
5337 } catch (RemoteException ex) {
5338 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5343 * asynchronously notify the assistant that a notification has been snoozed until a
5346 @GuardedBy("mNotificationLock")
5347 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5348 final String snoozeCriterionId) {
5349 TrimCache trimCache = new TrimCache(sbn);
5350 for (final ManagedServiceInfo info : getServices()) {
5351 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5352 mHandler.post(new Runnable() {
5355 final INotificationListener assistant =
5356 (INotificationListener) info.service;
5357 StatusBarNotificationHolder sbnHolder
5358 = new StatusBarNotificationHolder(sbnToPost);
5360 assistant.onNotificationSnoozedUntilContext(
5361 sbnHolder, snoozeCriterionId);
5362 } catch (RemoteException ex) {
5363 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5370 public boolean isEnabled() {
5371 return !getServices().isEmpty();
5375 public class NotificationListeners extends ManagedServices {
5376 static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
5378 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5380 public NotificationListeners(IPackageManager pm) {
5381 super(getContext(), mNotificationLock, mUserProfiles, pm);
5386 protected Config getConfig() {
5387 Config c = new Config();
5388 c.caption = "notification listener";
5389 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5390 c.xmlTag = TAG_ENABLED_NOTIFICATION_LISTENERS;
5391 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5392 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5393 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5394 c.clientLabel = R.string.notification_listener_binding_label;
5399 protected IInterface asInterface(IBinder binder) {
5400 return INotificationListener.Stub.asInterface(binder);
5404 protected boolean checkType(IInterface service) {
5405 return service instanceof INotificationListener;
5409 public void onServiceAdded(ManagedServiceInfo info) {
5410 final INotificationListener listener = (INotificationListener) info.service;
5411 final NotificationRankingUpdate update;
5412 synchronized (mNotificationLock) {
5413 update = makeRankingUpdateLocked(info);
5416 listener.onListenerConnected(update);
5417 } catch (RemoteException e) {
5423 @GuardedBy("mNotificationLock")
5424 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5425 if (removeDisabledHints(removed)) {
5426 updateListenerHintsLocked();
5427 updateEffectsSuppressorLocked();
5429 mLightTrimListeners.remove(removed);
5432 @GuardedBy("mNotificationLock")
5433 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5434 if (trim == TRIM_LIGHT) {
5435 mLightTrimListeners.add(info);
5437 mLightTrimListeners.remove(info);
5441 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5442 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5446 * asynchronously notify all listeners about a new notification
5449 * Also takes care of removing a notification that has been visible to a listener before,
5450 * but isn't anymore.
5452 @GuardedBy("mNotificationLock")
5453 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5454 // Lazily initialized snapshots of the notification.
5455 TrimCache trimCache = new TrimCache(sbn);
5457 for (final ManagedServiceInfo info : getServices()) {
5458 boolean sbnVisible = isVisibleToListener(sbn, info);
5459 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5460 // This notification hasn't been and still isn't visible -> ignore.
5461 if (!oldSbnVisible && !sbnVisible) {
5464 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5466 // This notification became invisible -> remove the old one.
5467 if (oldSbnVisible && !sbnVisible) {
5468 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5469 mHandler.post(new Runnable() {
5472 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5478 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5479 mHandler.post(new Runnable() {
5482 notifyPosted(info, sbnToPost, update);
5489 * asynchronously notify all listeners about a removed notification
5491 @GuardedBy("mNotificationLock")
5492 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5493 // make a copy in case changes are made to the underlying Notification object
5494 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5496 final StatusBarNotification sbnLight = sbn.cloneLight();
5497 for (final ManagedServiceInfo info : getServices()) {
5498 if (!isVisibleToListener(sbn, info)) {
5501 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5502 mHandler.post(new Runnable() {
5505 notifyRemoved(info, sbnLight, update, reason);
5512 * asynchronously notify all listeners about a reordering of notifications
5514 @GuardedBy("mNotificationLock")
5515 public void notifyRankingUpdateLocked() {
5516 for (final ManagedServiceInfo serviceInfo : getServices()) {
5517 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5520 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5521 mHandler.post(new Runnable() {
5524 notifyRankingUpdate(serviceInfo, update);
5530 @GuardedBy("mNotificationLock")
5531 public void notifyListenerHintsChangedLocked(final int hints) {
5532 for (final ManagedServiceInfo serviceInfo : getServices()) {
5533 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5536 mHandler.post(new Runnable() {
5539 notifyListenerHintsChanged(serviceInfo, hints);
5545 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5546 for (final ManagedServiceInfo serviceInfo : getServices()) {
5547 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5550 mHandler.post(new Runnable() {
5553 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5559 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5560 final NotificationChannel channel, final int modificationType) {
5561 if (channel == null) {
5564 for (final ManagedServiceInfo serviceInfo : getServices()) {
5565 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5569 mHandler.post(new Runnable() {
5572 if (hasCompanionDevice(serviceInfo)) {
5573 notifyNotificationChannelChanged(
5574 serviceInfo, pkg, user, channel, modificationType);
5581 protected void notifyNotificationChannelGroupChanged(
5582 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5583 final int modificationType) {
5584 if (group == null) {
5587 for (final ManagedServiceInfo serviceInfo : getServices()) {
5588 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5592 mHandler.post(new Runnable() {
5595 if (hasCompanionDevice(serviceInfo)) {
5596 notifyNotificationChannelGroupChanged(
5597 serviceInfo, pkg, user, group, modificationType);
5604 private void notifyPosted(final ManagedServiceInfo info,
5605 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5606 final INotificationListener listener = (INotificationListener) info.service;
5607 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5609 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5610 } catch (RemoteException ex) {
5611 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5615 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5616 NotificationRankingUpdate rankingUpdate, int reason) {
5617 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5620 final INotificationListener listener = (INotificationListener) info.service;
5621 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5623 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5624 } catch (RemoteException ex) {
5625 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5629 private void notifyRankingUpdate(ManagedServiceInfo info,
5630 NotificationRankingUpdate rankingUpdate) {
5631 final INotificationListener listener = (INotificationListener) info.service;
5633 listener.onNotificationRankingUpdate(rankingUpdate);
5634 } catch (RemoteException ex) {
5635 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5639 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5640 final INotificationListener listener = (INotificationListener) info.service;
5642 listener.onListenerHintsChanged(hints);
5643 } catch (RemoteException ex) {
5644 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5648 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5649 int interruptionFilter) {
5650 final INotificationListener listener = (INotificationListener) info.service;
5652 listener.onInterruptionFilterChanged(interruptionFilter);
5653 } catch (RemoteException ex) {
5654 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5658 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5659 final String pkg, final UserHandle user, final NotificationChannel channel,
5660 final int modificationType) {
5661 final INotificationListener listener = (INotificationListener) info.service;
5663 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5664 } catch (RemoteException ex) {
5665 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5669 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5670 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5671 final int modificationType) {
5672 final INotificationListener listener = (INotificationListener) info.service;
5674 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5675 } catch (RemoteException ex) {
5676 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5680 public boolean isListenerPackage(String packageName) {
5681 if (packageName == null) {
5684 // TODO: clean up locking object later
5685 synchronized (mNotificationLock) {
5686 for (final ManagedServiceInfo serviceInfo : getServices()) {
5687 if (packageName.equals(serviceInfo.component.getPackageName())) {
5696 public static final class DumpFilter {
5697 public boolean filtered = false;
5698 public String pkgFilter;
5701 public boolean stats;
5702 public boolean redact = true;
5703 public boolean proto = false;
5705 public static DumpFilter parseFromArguments(String[] args) {
5706 final DumpFilter filter = new DumpFilter();
5707 for (int ai = 0; ai < args.length; ai++) {
5708 final String a = args[ai];
5709 if ("--proto".equals(args[0])) {
5710 filter.proto = true;
5712 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5713 filter.redact = false;
5714 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5715 if (ai < args.length-1) {
5717 filter.pkgFilter = args[ai].trim().toLowerCase();
5718 if (filter.pkgFilter.isEmpty()) {
5719 filter.pkgFilter = null;
5721 filter.filtered = true;
5724 } else if ("--zen".equals(a) || "zen".equals(a)) {
5725 filter.filtered = true;
5727 } else if ("--stats".equals(a)) {
5728 filter.stats = true;
5729 if (ai < args.length-1) {
5731 filter.since = Long.parseLong(args[ai]);
5740 public boolean matches(StatusBarNotification sbn) {
5741 if (!filtered) return true;
5742 return zen ? true : sbn != null
5743 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5746 public boolean matches(ComponentName component) {
5747 if (!filtered) return true;
5748 return zen ? true : component != null && matches(component.getPackageName());
5751 public boolean matches(String pkg) {
5752 if (!filtered) return true;
5753 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5757 public String toString() {
5758 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5763 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5764 * binder without sending large amounts of data over a oneway transaction.
5766 private static final class StatusBarNotificationHolder
5767 extends IStatusBarNotificationHolder.Stub {
5768 private StatusBarNotification mValue;
5770 public StatusBarNotificationHolder(StatusBarNotification value) {
5774 /** Get the held value and clear it. This function should only be called once per holder */
5776 public StatusBarNotification get() {
5777 StatusBarNotification value = mValue;
5783 private class ShellCmd extends ShellCommand {
5784 public static final String USAGE = "help\n"
5785 + "allow_listener COMPONENT\n"
5786 + "disallow_listener COMPONENT\n"
5787 + "set_assistant COMPONENT\n"
5788 + "remove_assistant COMPONENT\n"
5789 + "allow_dnd PACKAGE\n"
5790 + "disallow_dnd PACKAGE";
5793 public int onCommand(String cmd) {
5795 return handleDefaultCommands(cmd);
5797 final PrintWriter pw = getOutPrintWriter();
5801 getBinderService().setNotificationPolicyAccessGranted(
5802 getNextArgRequired(), true);
5806 case "disallow_dnd": {
5807 getBinderService().setNotificationPolicyAccessGranted(
5808 getNextArgRequired(), false);
5811 case "allow_listener": {
5812 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5814 pw.println("Invalid listener - must be a ComponentName");
5817 getBinderService().setNotificationListenerAccessGranted(cn, true);
5820 case "disallow_listener": {
5821 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5823 pw.println("Invalid listener - must be a ComponentName");
5826 getBinderService().setNotificationListenerAccessGranted(cn, false);
5829 case "allow_assistant": {
5830 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5832 pw.println("Invalid assistant - must be a ComponentName");
5835 getBinderService().setNotificationAssistantAccessGranted(cn, true);
5838 case "disallow_assistant": {
5839 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5841 pw.println("Invalid assistant - must be a ComponentName");
5844 getBinderService().setNotificationAssistantAccessGranted(cn, false);
5849 return handleDefaultCommands(cmd);
5851 } catch (Exception e) {
5852 pw.println("Error occurred. Check logcat for details. " + e.getMessage());
5853 Slog.e(TAG, "Error running shell command", e);
5859 public void onHelp() {
5860 getOutPrintWriter().println(USAGE);