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.AudioAttributes;
97 import android.media.AudioManager;
98 import android.media.AudioManagerInternal;
99 import android.media.IRingtonePlayer;
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.FileNotFoundException;
191 import java.io.FileOutputStream;
192 import java.io.IOException;
193 import java.io.InputStream;
194 import java.io.OutputStream;
195 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 protected boolean mInCall = false;
308 private boolean mNotificationPulseEnabled;
310 private Uri mInCallNotificationUri;
311 private AudioAttributes mInCallNotificationAudioAttributes;
312 private float mInCallNotificationVolume;
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 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 update(ITransientNotification callback) {
575 this.callback = callback;
578 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
579 if (filter != null && !filter.matches(pkg)) return;
580 pw.println(prefix + this);
584 public final String toString()
586 return "ToastRecord{"
587 + Integer.toHexString(System.identityHashCode(this))
589 + " callback=" + callback
590 + " duration=" + duration;
595 final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
598 public void onSetDisabled(int status) {
599 synchronized (mNotificationLock) {
600 mDisableNotificationEffects =
601 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
602 if (disableNotificationEffects(null) != null) {
603 // cancel whatever's going on
604 long identity = Binder.clearCallingIdentity();
606 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
607 if (player != null) {
610 } catch (RemoteException e) {
612 Binder.restoreCallingIdentity(identity);
615 identity = Binder.clearCallingIdentity();
619 Binder.restoreCallingIdentity(identity);
626 public void onClearAll(int callingUid, int callingPid, int userId) {
627 synchronized (mNotificationLock) {
628 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
629 /*includeCurrentProfiles*/ true);
634 public void onNotificationClick(int callingUid, int callingPid, String key) {
635 synchronized (mNotificationLock) {
636 NotificationRecord r = mNotificationsByKey.get(key);
638 Log.w(TAG, "No notification with key: " + key);
641 final long now = System.currentTimeMillis();
642 MetricsLogger.action(r.getLogMaker(now)
643 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
644 .setType(MetricsEvent.TYPE_ACTION));
645 EventLogTags.writeNotificationClicked(key,
646 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
648 StatusBarNotification sbn = r.sbn;
649 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
650 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
651 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
657 public void onNotificationActionClick(int callingUid, int callingPid, String key,
659 synchronized (mNotificationLock) {
660 NotificationRecord r = mNotificationsByKey.get(key);
662 Log.w(TAG, "No notification with key: " + key);
665 final long now = System.currentTimeMillis();
666 MetricsLogger.action(r.getLogMaker(now)
667 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
668 .setType(MetricsEvent.TYPE_ACTION)
669 .setSubtype(actionIndex));
670 EventLogTags.writeNotificationActionClicked(key, actionIndex,
671 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
672 // TODO: Log action click via UsageStats.
677 public void onNotificationClear(int callingUid, int callingPid,
678 String pkg, String tag, int id, int userId) {
679 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
680 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
681 true, userId, REASON_CANCEL, null);
685 public void onPanelRevealed(boolean clearEffects, int items) {
686 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
687 MetricsLogger.histogram(getContext(), "note_load", items);
688 EventLogTags.writeNotificationPanelRevealed(items);
695 public void onPanelHidden() {
696 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
697 EventLogTags.writeNotificationPanelHidden();
701 public void clearEffects() {
702 synchronized (mNotificationLock) {
703 if (DBG) Slog.d(TAG, "clearEffects");
705 clearVibrateLocked();
711 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
712 int uid, int initialPid, String message, int userId) {
713 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
714 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
715 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
717 long ident = Binder.clearCallingIdentity();
719 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
720 "Bad notification posted from package " + pkg
722 } catch (RemoteException e) {
724 Binder.restoreCallingIdentity(ident);
728 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
729 NotificationVisibility[] noLongerVisibleKeys) {
730 synchronized (mNotificationLock) {
731 for (NotificationVisibility nv : newlyVisibleKeys) {
732 NotificationRecord r = mNotificationsByKey.get(nv.key);
733 if (r == null) continue;
734 r.setVisibility(true, nv.rank);
737 // Note that we might receive this event after notifications
738 // have already left the system, e.g. after dismissing from the
739 // shade. Hence not finding notifications in
740 // mNotificationsByKey is not an exceptional condition.
741 for (NotificationVisibility nv : noLongerVisibleKeys) {
742 NotificationRecord r = mNotificationsByKey.get(nv.key);
743 if (r == null) continue;
744 r.setVisibility(false, nv.rank);
751 public void onNotificationExpansionChanged(String key,
752 boolean userAction, boolean expanded) {
753 synchronized (mNotificationLock) {
754 NotificationRecord r = mNotificationsByKey.get(key);
756 r.stats.onExpansionChanged(userAction, expanded);
757 final long now = System.currentTimeMillis();
758 MetricsLogger.action(r.getLogMaker(now)
759 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
760 .setType(MetricsEvent.TYPE_DETAIL));
761 EventLogTags.writeNotificationExpansion(key,
762 userAction ? 1 : 0, expanded ? 1 : 0,
763 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
769 @GuardedBy("mNotificationLock")
770 private void clearSoundLocked() {
771 mSoundNotificationKey = null;
772 long identity = Binder.clearCallingIdentity();
774 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
775 if (player != null) {
778 } catch (RemoteException e) {
780 Binder.restoreCallingIdentity(identity);
784 @GuardedBy("mNotificationLock")
785 private void clearVibrateLocked() {
786 mVibrateNotificationKey = null;
787 long identity = Binder.clearCallingIdentity();
791 Binder.restoreCallingIdentity(identity);
795 @GuardedBy("mNotificationLock")
796 private void clearLightsLocked() {
799 updateLightsLocked();
802 protected final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
804 public void onReceive(Context context, Intent intent) {
805 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
806 mZenModeHelper.updateDefaultZenRules();
811 private final BroadcastReceiver mRestoreReceiver = new BroadcastReceiver() {
813 public void onReceive(Context context, Intent intent) {
814 if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
816 String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
817 String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
818 int restoredFromSdkInt = intent.getIntExtra(
819 Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0);
820 mListeners.onSettingRestored(
821 element, newValue, restoredFromSdkInt, getSendingUserId());
822 mConditionProviders.onSettingRestored(
823 element, newValue, restoredFromSdkInt, getSendingUserId());
824 } catch (Exception e) {
825 Slog.wtf(TAG, "Cannot restore managed services from settings", e);
831 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
833 public void onReceive(Context context, Intent intent) {
834 String action = intent.getAction();
835 if (action == null) {
838 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
839 final NotificationRecord record;
840 synchronized (mNotificationLock) {
841 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
843 if (record != null) {
844 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
845 record.sbn.getPackageName(), record.sbn.getTag(),
846 record.sbn.getId(), 0,
847 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
848 REASON_TIMEOUT, null);
854 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
856 public void onReceive(Context context, Intent intent) {
857 String action = intent.getAction();
858 if (action == null) {
862 boolean queryRestart = false;
863 boolean queryRemove = false;
864 boolean packageChanged = false;
865 boolean cancelNotifications = true;
866 int reason = REASON_PACKAGE_CHANGED;
868 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
869 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
870 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
871 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
872 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
873 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
874 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
875 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
876 UserHandle.USER_ALL);
877 String pkgList[] = null;
878 int uidList[] = null;
879 boolean removingPackage = queryRemove &&
880 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
881 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
882 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
883 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
884 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
885 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
886 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
887 reason = REASON_PACKAGE_SUSPENDED;
888 } else if (queryRestart) {
889 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
890 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
892 Uri uri = intent.getData();
896 String pkgName = uri.getSchemeSpecificPart();
897 if (pkgName == null) {
900 if (packageChanged) {
901 // We cancel notifications for packages which have just been disabled
903 final int enabled = mPackageManager.getApplicationEnabledSetting(
905 changeUserId != UserHandle.USER_ALL ? changeUserId :
906 UserHandle.USER_SYSTEM);
907 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
908 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
909 cancelNotifications = false;
911 } catch (IllegalArgumentException e) {
912 // Package doesn't exist; probably racing with uninstall.
913 // cancelNotifications is already true, so nothing to do here.
915 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
917 } catch (RemoteException e) {
918 // Failed to talk to PackageManagerService Should never happen!
921 pkgList = new String[]{pkgName};
922 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
924 if (pkgList != null && (pkgList.length > 0)) {
925 for (String pkgName : pkgList) {
926 if (cancelNotifications) {
927 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
928 !queryRestart, changeUserId, reason, null);
932 mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
933 mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
934 mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
935 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
941 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
943 public void onReceive(Context context, Intent intent) {
944 String action = intent.getAction();
946 if (action.equals(Intent.ACTION_SCREEN_ON)) {
947 // Keep track of screen on/off state, but do not turn off the notification light
948 // until user passes through the lock screen or views the notification.
950 updateNotificationPulse();
951 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
953 updateNotificationPulse();
954 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
955 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
956 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
957 updateNotificationPulse();
958 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
959 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
960 if (userHandle >= 0) {
961 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
962 REASON_USER_STOPPED, null);
964 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
965 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
966 if (userHandle >= 0) {
967 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
968 REASON_PROFILE_TURNED_OFF, null);
970 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
971 // turn off LED when user passes through lock screen
972 mNotificationLight.turnOff();
973 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
974 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
975 // reload per-user settings
976 mSettingsObserver.update(null);
977 mUserProfiles.updateCache(context);
978 // Refresh managed services
979 mConditionProviders.onUserSwitched(user);
980 mListeners.onUserSwitched(user);
981 mAssistants.onUserSwitched(user);
982 mZenModeHelper.onUserSwitched(user);
983 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
984 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
985 if (userId != USER_NULL) {
986 mUserProfiles.updateCache(context);
987 readDefaultApprovedServices(userId);
989 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
990 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
991 mZenModeHelper.onUserRemoved(user);
992 mRankingHelper.onUserRemoved(user);
994 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
995 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
996 mConditionProviders.onUserUnlocked(user);
997 mListeners.onUserUnlocked(user);
998 mAssistants.onUserUnlocked(user);
999 mZenModeHelper.onUserUnlocked(user);
1004 private final class SettingsObserver extends ContentObserver {
1005 private final Uri NOTIFICATION_BADGING_URI
1006 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
1007 private final Uri NOTIFICATION_LIGHT_PULSE_URI
1008 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
1009 private final Uri NOTIFICATION_RATE_LIMIT_URI
1010 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
1012 SettingsObserver(Handler handler) {
1017 ContentResolver resolver = getContext().getContentResolver();
1018 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
1019 false, this, UserHandle.USER_ALL);
1020 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
1021 false, this, UserHandle.USER_ALL);
1022 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
1023 false, this, UserHandle.USER_ALL);
1027 @Override public void onChange(boolean selfChange, Uri uri) {
1031 public void update(Uri uri) {
1032 ContentResolver resolver = getContext().getContentResolver();
1033 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
1034 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
1035 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
1036 if (mNotificationPulseEnabled != pulseEnabled) {
1037 mNotificationPulseEnabled = pulseEnabled;
1038 updateNotificationPulse();
1041 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
1042 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
1043 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
1045 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
1046 mRankingHelper.updateBadgingEnabled();
1051 private SettingsObserver mSettingsObserver;
1052 protected ZenModeHelper mZenModeHelper;
1054 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1055 int[] ar = r.getIntArray(resid);
1059 final int len = ar.length > maxlen ? maxlen : ar.length;
1060 long[] out = new long[len];
1061 for (int i=0; i<len; i++) {
1067 public NotificationManagerService(Context context) {
1069 Notification.processWhitelistToken = WHITELIST_TOKEN;
1072 // TODO - replace these methods with a single VisibleForTesting constructor
1074 void setAudioManager(AudioManager audioMananger) {
1075 mAudioManager = audioMananger;
1079 void setVibrator(Vibrator vibrator) {
1080 mVibrator = vibrator;
1084 void setLights(Light light) {
1085 mNotificationLight = light;
1086 mAttentionLight = light;
1087 mNotificationPulseEnabled = true;
1091 void setScreenOn(boolean on) {
1096 int getNotificationRecordCount() {
1097 synchronized (mNotificationLock) {
1098 int count = mNotificationList.size() + mNotificationsByKey.size()
1099 + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
1100 // subtract duplicates
1101 for (NotificationRecord posted : mNotificationList) {
1102 if (mNotificationsByKey.containsKey(posted.getKey())) {
1105 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
1114 void clearNotifications() {
1115 mEnqueuedNotifications.clear();
1116 mNotificationList.clear();
1117 mNotificationsByKey.clear();
1118 mSummaryByGroupKey.clear();
1122 void addNotification(NotificationRecord r) {
1123 mNotificationList.add(r);
1124 mNotificationsByKey.put(r.sbn.getKey(), r);
1125 if (r.sbn.isGroup()) {
1126 mSummaryByGroupKey.put(r.getGroupKey(), r);
1131 void addEnqueuedNotification(NotificationRecord r) {
1132 mEnqueuedNotifications.add(r);
1136 void setSystemReady(boolean systemReady) {
1137 mSystemReady = systemReady;
1141 void setHandler(WorkerHandler handler) {
1146 void setFallbackVibrationPattern(long[] vibrationPattern) {
1147 mFallbackVibrationPattern = vibrationPattern;
1151 void setPackageManager(IPackageManager packageManager) {
1152 mPackageManager = packageManager;
1156 void setRankingHelper(RankingHelper rankingHelper) {
1157 mRankingHelper = rankingHelper;
1161 void setRankingHandler(RankingHandler rankingHandler) {
1162 mRankingHandler = rankingHandler;
1166 void setIsTelevision(boolean isTelevision) {
1167 mIsTelevision = isTelevision;
1171 void setUsageStats(NotificationUsageStats us) {
1175 // TODO: All tests should use this init instead of the one-off setters above.
1177 void init(Looper looper, IPackageManager packageManager,
1178 PackageManager packageManagerClient,
1179 LightsManager lightsManager, NotificationListeners notificationListeners,
1180 NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
1181 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1182 NotificationUsageStats usageStats, AtomicFile policyFile,
1183 ActivityManager activityManager, GroupHelper groupHelper) {
1184 Resources resources = getContext().getResources();
1185 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1186 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1187 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1189 mAm = ActivityManager.getService();
1190 mPackageManager = packageManager;
1191 mPackageManagerClient = packageManagerClient;
1192 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1193 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1194 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1195 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1196 mCompanionManager = companionManager;
1197 mActivityManager = activityManager;
1199 mHandler = new WorkerHandler(looper);
1200 mRankingThread.start();
1201 String[] extractorNames;
1203 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1204 } catch (Resources.NotFoundException e) {
1205 extractorNames = new String[0];
1207 mUsageStats = usageStats;
1208 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1209 mRankingHelper = new RankingHelper(getContext(),
1210 getContext().getPackageManager(),
1214 mConditionProviders = conditionProviders;
1215 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1216 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1218 public void onConfigChanged() {
1223 void onZenModeChanged() {
1224 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1225 getContext().sendBroadcastAsUser(
1226 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1227 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1228 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1229 synchronized (mNotificationLock) {
1230 updateInterruptionFilterLocked();
1235 void onPolicyChanged() {
1236 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1239 mSnoozeHelper = snoozeHelper;
1240 mGroupHelper = groupHelper;
1242 // This is a ManagedServices object that keeps track of the listeners.
1243 mListeners = notificationListeners;
1245 // This is a MangedServices object that keeps track of the assistant.
1246 mAssistants = notificationAssistants;
1248 mPolicyFile = policyFile;
1251 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1252 if (mStatusBar != null) {
1253 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1256 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1257 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1259 mFallbackVibrationPattern = getLongArray(resources,
1260 R.array.config_notificationFallbackVibePattern,
1261 VIBRATE_PATTERN_MAXLEN,
1262 DEFAULT_VIBRATE_PATTERN);
1264 mInCallNotificationUri = Uri.parse("file://" +
1265 resources.getString(R.string.config_inCallNotificationSound));
1266 mInCallNotificationAudioAttributes = new AudioAttributes.Builder()
1267 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
1268 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
1269 .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
1271 mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
1273 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1275 // Don't start allowing notifications until the setup wizard has run once.
1276 // After that, including subsequent boots, init with notifications turned on.
1277 // This works on the first boot because the setup wizard will toggle this
1278 // flag at least once and we'll go back to 0 after that.
1279 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1280 Settings.Global.DEVICE_PROVISIONED, 0)) {
1281 mDisableNotificationEffects = true;
1283 mZenModeHelper.initZenMode();
1284 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1286 mUserProfiles.updateCache(getContext());
1287 listenForCallState();
1289 mSettingsObserver = new SettingsObserver(mHandler);
1291 mArchive = new Archive(resources.getInteger(
1292 R.integer.config_notificationServiceArchiveSize));
1294 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1295 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1299 public void onStart() {
1300 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1302 public void repost(int userId, NotificationRecord r) {
1305 Slog.d(TAG, "Reposting " + r.getKey());
1307 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1308 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1309 r.sbn.getNotification(), userId);
1310 } catch (Exception e) {
1311 Slog.e(TAG, "Cannot un-snooze notification", e);
1316 final File systemDir = new File(Environment.getDataDirectory(), "system");
1318 init(Looper.myLooper(),
1319 AppGlobals.getPackageManager(), getContext().getPackageManager(),
1320 getLocalService(LightsManager.class),
1321 new NotificationListeners(AppGlobals.getPackageManager()),
1322 new NotificationAssistants(AppGlobals.getPackageManager()),
1323 new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
1324 null, snoozeHelper, new NotificationUsageStats(getContext()),
1325 new AtomicFile(new File(systemDir, "notification_policy.xml")),
1326 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
1329 // register for various Intents
1330 IntentFilter filter = new IntentFilter();
1331 filter.addAction(Intent.ACTION_SCREEN_ON);
1332 filter.addAction(Intent.ACTION_SCREEN_OFF);
1333 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1334 filter.addAction(Intent.ACTION_USER_PRESENT);
1335 filter.addAction(Intent.ACTION_USER_STOPPED);
1336 filter.addAction(Intent.ACTION_USER_SWITCHED);
1337 filter.addAction(Intent.ACTION_USER_ADDED);
1338 filter.addAction(Intent.ACTION_USER_REMOVED);
1339 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1340 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1341 getContext().registerReceiver(mIntentReceiver, filter);
1343 IntentFilter pkgFilter = new IntentFilter();
1344 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1345 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1346 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1347 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1348 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1349 pkgFilter.addDataScheme("package");
1350 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1353 IntentFilter suspendedPkgFilter = new IntentFilter();
1354 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1355 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1356 suspendedPkgFilter, null, null);
1358 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1359 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1362 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1363 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1364 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1366 IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
1367 getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
1369 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
1370 getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
1372 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1373 publishLocalService(NotificationManagerInternal.class, mInternalService);
1376 private GroupHelper getGroupHelper() {
1377 return new GroupHelper(new GroupHelper.Callback() {
1379 public void addAutoGroup(String key) {
1380 synchronized (mNotificationLock) {
1381 addAutogroupKeyLocked(key);
1386 public void removeAutoGroup(String key) {
1387 synchronized (mNotificationLock) {
1388 removeAutogroupKeyLocked(key);
1393 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1394 createAutoGroupSummary(userId, pkg, triggeringKey);
1398 public void removeAutoGroupSummary(int userId, String pkg) {
1399 synchronized (mNotificationLock) {
1400 clearAutogroupSummaryLocked(userId, pkg);
1406 private void sendRegisteredOnlyBroadcast(String action) {
1407 getContext().sendBroadcastAsUser(new Intent(action)
1408 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1412 public void onBootPhase(int phase) {
1413 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1414 // no beeping until we're basically done booting
1415 mSystemReady = true;
1417 // Grab our optional AudioService
1418 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1419 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1420 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1421 mZenModeHelper.onSystemReady();
1422 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1423 // This observer will force an update when observe is called, causing us to
1424 // bind to listener services.
1425 mSettingsObserver.observe();
1426 mListeners.onBootPhaseAppsCanStart();
1427 mAssistants.onBootPhaseAppsCanStart();
1428 mConditionProviders.onBootPhaseAppsCanStart();
1432 @GuardedBy("mNotificationLock")
1433 private void updateListenerHintsLocked() {
1434 final int hints = calculateHints();
1435 if (hints == mListenerHints) return;
1436 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1437 mListenerHints = hints;
1438 scheduleListenerHintsChanged(hints);
1441 @GuardedBy("mNotificationLock")
1442 private void updateEffectsSuppressorLocked() {
1443 final long updatedSuppressedEffects = calculateSuppressedEffects();
1444 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1445 final List<ComponentName> suppressors = getSuppressors();
1446 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1447 mEffectsSuppressors = suppressors;
1448 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1449 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1452 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1453 boolean fromListener) {
1454 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1456 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1457 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1460 mRankingHelper.updateNotificationChannel(pkg, uid, channel);
1462 if (!fromListener) {
1463 final NotificationChannel modifiedChannel =
1464 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1465 mListeners.notifyNotificationChannelChanged(
1466 pkg, UserHandle.getUserHandleForUid(uid),
1467 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1473 private ArrayList<ComponentName> getSuppressors() {
1474 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1475 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1476 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1478 for (ManagedServiceInfo info : serviceInfoList) {
1479 names.add(info.component);
1486 private boolean removeDisabledHints(ManagedServiceInfo info) {
1487 return removeDisabledHints(info, 0);
1490 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1491 boolean removed = false;
1493 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1494 final int hint = mListenersDisablingEffects.keyAt(i);
1495 final ArraySet<ManagedServiceInfo> listeners =
1496 mListenersDisablingEffects.valueAt(i);
1498 if (hints == 0 || (hint & hints) == hint) {
1499 removed = removed || listeners.remove(info);
1506 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1507 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1508 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1511 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1512 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1515 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1516 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1520 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1521 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1522 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1525 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1526 hintListeners.add(info);
1529 private int calculateHints() {
1531 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1532 int hint = mListenersDisablingEffects.keyAt(i);
1533 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1535 if (!serviceInfoList.isEmpty()) {
1543 private long calculateSuppressedEffects() {
1544 int hints = calculateHints();
1545 long suppressedEffects = 0;
1547 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1548 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1551 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1552 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1555 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1556 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1559 return suppressedEffects;
1562 @GuardedBy("mNotificationLock")
1563 private void updateInterruptionFilterLocked() {
1564 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1565 if (interruptionFilter == mInterruptionFilter) return;
1566 mInterruptionFilter = interruptionFilter;
1567 scheduleInterruptionFilterChanged(interruptionFilter);
1571 INotificationManager getBinderService() {
1572 return INotificationManager.Stub.asInterface(mService);
1576 NotificationManagerInternal getInternalService() {
1577 return mInternalService;
1580 private final IBinder mService = new INotificationManager.Stub() {
1582 // ============================================================================
1585 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1588 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1589 + " duration=" + duration);
1592 if (pkg == null || callback == null) {
1593 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1596 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1597 final boolean isPackageSuspended =
1598 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1600 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1601 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1602 || isPackageSuspended)) {
1603 Slog.e(TAG, "Suppressing toast from package " + pkg
1604 + (isPackageSuspended
1605 ? " due to package suspended by administrator."
1606 : " by user request."));
1610 synchronized (mToastQueue) {
1611 int callingPid = Binder.getCallingPid();
1612 long callingId = Binder.clearCallingIdentity();
1616 // All packages aside from the android package can enqueue one toast at a time
1617 if (!isSystemToast) {
1618 index = indexOfToastPackageLocked(pkg);
1620 index = indexOfToastLocked(pkg, callback);
1623 // If the package already has a toast, we update its toast
1624 // in the queue, we don't move it to the end of the queue.
1626 record = mToastQueue.get(index);
1627 record.update(duration);
1628 record.update(callback);
1630 Binder token = new Binder();
1631 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1632 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1633 mToastQueue.add(record);
1634 index = mToastQueue.size() - 1;
1636 keepProcessAliveIfNeededLocked(callingPid);
1637 // If it's at index 0, it's the current toast. It doesn't matter if it's
1638 // new or just been updated. Call back and tell it to show itself.
1639 // If the callback fails, this will remove it from the list, so don't
1640 // assume that it's valid after this.
1642 showNextToastLocked();
1645 Binder.restoreCallingIdentity(callingId);
1651 public void cancelToast(String pkg, ITransientNotification callback) {
1652 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1654 if (pkg == null || callback == null) {
1655 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1659 synchronized (mToastQueue) {
1660 long callingId = Binder.clearCallingIdentity();
1662 int index = indexOfToastLocked(pkg, callback);
1664 cancelToastLocked(index);
1666 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1667 + " callback=" + callback);
1670 Binder.restoreCallingIdentity(callingId);
1676 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1677 Notification notification, int userId) throws RemoteException {
1678 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1679 Binder.getCallingPid(), tag, id, notification, userId);
1683 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1684 checkCallerIsSystemOrSameApp(pkg);
1685 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1686 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1687 // Don't allow client applications to cancel foreground service notis or autobundled
1689 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1690 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1691 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1692 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1696 public void cancelAllNotifications(String pkg, int userId) {
1697 checkCallerIsSystemOrSameApp(pkg);
1699 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1700 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1702 // Calling from user space, don't allow the canceling of actively
1703 // running foreground services.
1704 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1705 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1706 REASON_APP_CANCEL_ALL, null);
1710 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1711 checkCallerIsSystem();
1713 mRankingHelper.setEnabled(pkg, uid, enabled);
1714 // Now, cancel any outstanding notifications that are part of a just-disabled app
1716 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1717 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1723 * Use this when you just want to know if notifications are OK for this package.
1726 public boolean areNotificationsEnabled(String pkg) {
1727 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1731 * Use this when you just want to know if notifications are OK for this package.
1734 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1735 checkCallerIsSystemOrSameApp(pkg);
1737 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1741 public int getPackageImportance(String pkg) {
1742 checkCallerIsSystemOrSameApp(pkg);
1743 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1747 public boolean canShowBadge(String pkg, int uid) {
1748 checkCallerIsSystem();
1749 return mRankingHelper.canShowBadge(pkg, uid);
1753 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1754 checkCallerIsSystem();
1755 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1760 public void createNotificationChannelGroups(String pkg,
1761 ParceledListSlice channelGroupList) throws RemoteException {
1762 checkCallerIsSystemOrSameApp(pkg);
1763 List<NotificationChannelGroup> groups = channelGroupList.getList();
1764 final int groupSize = groups.size();
1765 for (int i = 0; i < groupSize; i++) {
1766 final NotificationChannelGroup group = groups.get(i);
1767 Preconditions.checkNotNull(group, "group in list is null");
1768 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1769 true /* fromTargetApp */);
1770 mListeners.notifyNotificationChannelGroupChanged(pkg,
1771 UserHandle.of(UserHandle.getCallingUserId()), group,
1772 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1777 private void createNotificationChannelsImpl(String pkg, int uid,
1778 ParceledListSlice channelsList) {
1779 List<NotificationChannel> channels = channelsList.getList();
1780 final int channelsSize = channels.size();
1781 for (int i = 0; i < channelsSize; i++) {
1782 final NotificationChannel channel = channels.get(i);
1783 Preconditions.checkNotNull(channel, "channel in list is null");
1784 mRankingHelper.createNotificationChannel(pkg, uid, channel,
1785 true /* fromTargetApp */);
1786 mListeners.notifyNotificationChannelChanged(pkg,
1787 UserHandle.getUserHandleForUid(uid),
1788 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1789 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1795 public void createNotificationChannels(String pkg,
1796 ParceledListSlice channelsList) throws RemoteException {
1797 checkCallerIsSystemOrSameApp(pkg);
1798 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1802 public void createNotificationChannelsForPackage(String pkg, int uid,
1803 ParceledListSlice channelsList) throws RemoteException {
1804 checkCallerIsSystem();
1805 createNotificationChannelsImpl(pkg, uid, channelsList);
1809 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1810 checkCallerIsSystemOrSameApp(pkg);
1811 return mRankingHelper.getNotificationChannel(
1812 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1816 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1817 String channelId, boolean includeDeleted) {
1818 checkCallerIsSystem();
1819 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1823 public void deleteNotificationChannel(String pkg, String channelId) {
1824 checkCallerIsSystemOrSameApp(pkg);
1825 final int callingUid = Binder.getCallingUid();
1826 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1827 throw new IllegalArgumentException("Cannot delete default channel");
1829 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1830 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1831 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1832 mListeners.notifyNotificationChannelChanged(pkg,
1833 UserHandle.getUserHandleForUid(callingUid),
1834 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1835 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1840 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1842 checkCallerIsSystemOrSameApp(pkg);
1843 return new ParceledListSlice<>(new ArrayList(
1844 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1848 public void deleteNotificationChannelGroup(String pkg, String groupId) {
1849 checkCallerIsSystemOrSameApp(pkg);
1851 final int callingUid = Binder.getCallingUid();
1852 NotificationChannelGroup groupToDelete =
1853 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1854 if (groupToDelete != null) {
1855 List<NotificationChannel> deletedChannels =
1856 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1857 for (int i = 0; i < deletedChannels.size(); i++) {
1858 final NotificationChannel deletedChannel = deletedChannels.get(i);
1859 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1861 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1863 mListeners.notifyNotificationChannelChanged(pkg,
1864 UserHandle.getUserHandleForUid(callingUid),
1866 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1868 mListeners.notifyNotificationChannelGroupChanged(
1869 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1870 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1876 public void updateNotificationChannelForPackage(String pkg, int uid,
1877 NotificationChannel channel) {
1878 enforceSystemOrSystemUI("Caller not system or systemui");
1879 Preconditions.checkNotNull(channel);
1880 updateNotificationChannelInt(pkg, uid, channel, false);
1884 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1885 int uid, boolean includeDeleted) {
1886 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1887 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1891 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1892 boolean includeDeleted) {
1893 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1894 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1899 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1900 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1901 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1905 public int getDeletedChannelCount(String pkg, int uid) {
1906 enforceSystemOrSystemUI("getDeletedChannelCount");
1907 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1911 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1912 String pkg, int uid, boolean includeDeleted) {
1913 checkCallerIsSystem();
1914 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1918 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1919 String groupId, String pkg, int uid) {
1920 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1921 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1925 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1926 checkCallerIsSystemOrSameApp(pkg);
1927 return mRankingHelper.getNotificationChannels(
1928 pkg, Binder.getCallingUid(), false /* includeDeleted */);
1932 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1933 checkCallerIsSystem();
1935 // Cancel posted notifications
1936 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1937 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1939 final String[] packages = new String[] {packageName};
1940 final int[] uids = new int[] {uid};
1942 // Listener & assistant
1943 mListeners.onPackagesChanged(true, packages, uids);
1944 mAssistants.onPackagesChanged(true, packages, uids);
1947 mConditionProviders.onPackagesChanged(true, packages, uids);
1949 // Reset notification preferences
1951 mRankingHelper.onPackagesChanged(
1952 true, UserHandle.getCallingUserId(), packages, uids);
1960 * System-only API for getting a list of current (i.e. not cleared) notifications.
1962 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1963 * @returns A list of all the notifications, in natural order.
1966 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1967 // enforce() will ensure the calling uid has the correct permission
1968 getContext().enforceCallingOrSelfPermission(
1969 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1970 "NotificationManagerService.getActiveNotifications");
1972 StatusBarNotification[] tmp = null;
1973 int uid = Binder.getCallingUid();
1975 // noteOp will check to make sure the callingPkg matches the uid
1976 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1977 == AppOpsManager.MODE_ALLOWED) {
1978 synchronized (mNotificationLock) {
1979 tmp = new StatusBarNotification[mNotificationList.size()];
1980 final int N = mNotificationList.size();
1981 for (int i=0; i<N; i++) {
1982 tmp[i] = mNotificationList.get(i).sbn;
1990 * Public API for getting a list of current notifications for the calling package/uid.
1992 * Note that since notification posting is done asynchronously, this will not return
1993 * notifications that are in the process of being posted.
1995 * @returns A list of all the package's notifications, in natural order.
1998 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1999 int incomingUserId) {
2000 checkCallerIsSystemOrSameApp(pkg);
2001 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
2002 Binder.getCallingUid(), incomingUserId, true, false,
2003 "getAppActiveNotifications", pkg);
2004 synchronized (mNotificationLock) {
2005 final ArrayMap<String, StatusBarNotification> map
2006 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
2007 final int N = mNotificationList.size();
2008 for (int i = 0; i < N; i++) {
2009 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2010 mNotificationList.get(i).sbn);
2012 map.put(sbn.getKey(), sbn);
2015 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
2016 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
2018 map.put(sbn.getKey(), sbn);
2021 final int M = mEnqueuedNotifications.size();
2022 for (int i = 0; i < M; i++) {
2023 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
2024 mEnqueuedNotifications.get(i).sbn);
2026 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
2029 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
2030 list.addAll(map.values());
2031 return new ParceledListSlice<StatusBarNotification>(list);
2035 private StatusBarNotification sanitizeSbn(String pkg, int userId,
2036 StatusBarNotification sbn) {
2037 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
2038 // We could pass back a cloneLight() but clients might get confused and
2039 // try to send this thing back to notify() again, which would not work
2041 return new StatusBarNotification(
2042 sbn.getPackageName(),
2044 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
2045 sbn.getNotification().clone(),
2046 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
2052 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
2054 * Requires ACCESS_NOTIFICATIONS which is signature|system.
2057 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
2058 // enforce() will ensure the calling uid has the correct permission
2059 getContext().enforceCallingOrSelfPermission(
2060 android.Manifest.permission.ACCESS_NOTIFICATIONS,
2061 "NotificationManagerService.getHistoricalNotifications");
2063 StatusBarNotification[] tmp = null;
2064 int uid = Binder.getCallingUid();
2066 // noteOp will check to make sure the callingPkg matches the uid
2067 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
2068 == AppOpsManager.MODE_ALLOWED) {
2069 synchronized (mArchive) {
2070 tmp = mArchive.getArray(count);
2077 * Register a listener binder directly with the notification manager.
2079 * Only works with system callers. Apps should extend
2080 * {@link android.service.notification.NotificationListenerService}.
2083 public void registerListener(final INotificationListener listener,
2084 final ComponentName component, final int userid) {
2085 enforceSystemOrSystemUI("INotificationManager.registerListener");
2086 mListeners.registerService(listener, component, userid);
2090 * Remove a listener binder directly
2093 public void unregisterListener(INotificationListener token, int userid) {
2094 mListeners.unregisterService(token, userid);
2098 * Allow an INotificationListener to simulate a "clear all" operation.
2100 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2102 * @param token The binder for the listener, to check that the caller is allowed
2105 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
2106 final int callingUid = Binder.getCallingUid();
2107 final int callingPid = Binder.getCallingPid();
2108 long identity = Binder.clearCallingIdentity();
2110 synchronized (mNotificationLock) {
2111 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2113 final int N = keys.length;
2114 for (int i = 0; i < N; i++) {
2115 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2116 if (r == null) continue;
2117 final int userId = r.sbn.getUserId();
2118 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2119 !mUserProfiles.isCurrentProfile(userId)) {
2120 throw new SecurityException("Disallowed call from listener: "
2123 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2124 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2128 cancelAllLocked(callingUid, callingPid, info.userid,
2129 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
2133 Binder.restoreCallingIdentity(identity);
2138 * Handle request from an approved listener to re-enable itself.
2140 * @param component The componenet to be re-enabled, caller must match package.
2143 public void requestBindListener(ComponentName component) {
2144 checkCallerIsSystemOrSameApp(component.getPackageName());
2145 long identity = Binder.clearCallingIdentity();
2147 ManagedServices manager =
2148 mAssistants.isComponentEnabledForCurrentProfiles(component)
2151 manager.setComponentState(component, true);
2153 Binder.restoreCallingIdentity(identity);
2158 public void requestUnbindListener(INotificationListener token) {
2159 long identity = Binder.clearCallingIdentity();
2161 // allow bound services to disable themselves
2162 synchronized (mNotificationLock) {
2163 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2164 info.getOwner().setComponentState(info.component, false);
2167 Binder.restoreCallingIdentity(identity);
2172 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2173 long identity = Binder.clearCallingIdentity();
2175 synchronized (mNotificationLock) {
2176 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2178 final int N = keys.length;
2179 for (int i = 0; i < N; i++) {
2180 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2181 if (r == null) continue;
2182 final int userId = r.sbn.getUserId();
2183 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2184 !mUserProfiles.isCurrentProfile(userId)) {
2185 throw new SecurityException("Disallowed call from listener: "
2189 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2190 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2191 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2193 UsageEvents.Event.USER_INTERACTION);
2200 Binder.restoreCallingIdentity(identity);
2205 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2207 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2209 * @param info The binder for the listener, to check that the caller is allowed
2211 @GuardedBy("mNotificationLock")
2212 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2213 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2214 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2215 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2217 userId, REASON_LISTENER_CANCEL, info);
2221 * Allow an INotificationListener to snooze a single notification until a context.
2223 * @param token The binder for the listener, to check that the caller is allowed
2226 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2227 String key, String snoozeCriterionId) {
2228 long identity = Binder.clearCallingIdentity();
2230 synchronized (mNotificationLock) {
2231 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2232 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2235 Binder.restoreCallingIdentity(identity);
2240 * Allow an INotificationListener to snooze a single notification until a time.
2242 * @param token The binder for the listener, to check that the caller is allowed
2245 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2247 long identity = Binder.clearCallingIdentity();
2249 synchronized (mNotificationLock) {
2250 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2251 snoozeNotificationInt(key, duration, null, info);
2254 Binder.restoreCallingIdentity(identity);
2259 * Allows the notification assistant to un-snooze a single notification.
2261 * @param token The binder for the assistant, to check that the caller is allowed
2264 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2265 long identity = Binder.clearCallingIdentity();
2267 synchronized (mNotificationLock) {
2268 final ManagedServiceInfo info =
2269 mAssistants.checkServiceTokenLocked(token);
2270 unsnoozeNotificationInt(key, info);
2273 Binder.restoreCallingIdentity(identity);
2278 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2280 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2282 * @param token The binder for the listener, to check that the caller is allowed
2285 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2286 String tag, int id) {
2287 final int callingUid = Binder.getCallingUid();
2288 final int callingPid = Binder.getCallingPid();
2289 long identity = Binder.clearCallingIdentity();
2291 synchronized (mNotificationLock) {
2292 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2293 if (info.supportsProfiles()) {
2294 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2295 + "from " + info.component
2296 + " use cancelNotification(key) instead.");
2298 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2299 pkg, tag, id, info.userid);
2303 Binder.restoreCallingIdentity(identity);
2308 * Allow an INotificationListener to request the list of outstanding notifications seen by
2309 * the current user. Useful when starting up, after which point the listener callbacks
2312 * @param token The binder for the listener, to check that the caller is allowed
2313 * @param keys An array of notification keys to fetch, or null to fetch everything
2314 * @returns The return value will contain the notifications specified in keys, in that
2315 * order, or if keys is null, all the notifications, in natural order.
2318 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2319 INotificationListener token, String[] keys, int trim) {
2320 synchronized (mNotificationLock) {
2321 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2322 final boolean getKeys = keys != null;
2323 final int N = getKeys ? keys.length : mNotificationList.size();
2324 final ArrayList<StatusBarNotification> list
2325 = new ArrayList<StatusBarNotification>(N);
2326 for (int i=0; i<N; i++) {
2327 final NotificationRecord r = getKeys
2328 ? mNotificationsByKey.get(keys[i])
2329 : mNotificationList.get(i);
2330 if (r == null) continue;
2331 StatusBarNotification sbn = r.sbn;
2332 if (!isVisibleToListener(sbn, info)) continue;
2333 StatusBarNotification sbnToSend =
2334 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2335 list.add(sbnToSend);
2337 return new ParceledListSlice<StatusBarNotification>(list);
2342 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2343 * seen by the current user. Useful when starting up, after which point the listener
2344 * callbacks should be used.
2346 * @param token The binder for the listener, to check that the caller is allowed
2347 * @returns The return value will contain the notifications specified in keys, in that
2348 * order, or if keys is null, all the notifications, in natural order.
2351 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2352 INotificationListener token, int trim) {
2353 synchronized (mNotificationLock) {
2354 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2355 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2356 final int N = snoozedRecords.size();
2357 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2358 for (int i=0; i < N; i++) {
2359 final NotificationRecord r = snoozedRecords.get(i);
2360 if (r == null) continue;
2361 StatusBarNotification sbn = r.sbn;
2362 if (!isVisibleToListener(sbn, info)) continue;
2363 StatusBarNotification sbnToSend =
2364 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2365 list.add(sbnToSend);
2367 return new ParceledListSlice<>(list);
2372 public void requestHintsFromListener(INotificationListener token, int hints) {
2373 final long identity = Binder.clearCallingIdentity();
2375 synchronized (mNotificationLock) {
2376 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2377 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2378 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2379 | HINT_HOST_DISABLE_CALL_EFFECTS;
2380 final boolean disableEffects = (hints & disableEffectsMask) != 0;
2381 if (disableEffects) {
2382 addDisabledHints(info, hints);
2384 removeDisabledHints(info, hints);
2386 updateListenerHintsLocked();
2387 updateEffectsSuppressorLocked();
2390 Binder.restoreCallingIdentity(identity);
2395 public int getHintsFromListener(INotificationListener token) {
2396 synchronized (mNotificationLock) {
2397 return mListenerHints;
2402 public void requestInterruptionFilterFromListener(INotificationListener token,
2403 int interruptionFilter) throws RemoteException {
2404 final long identity = Binder.clearCallingIdentity();
2406 synchronized (mNotificationLock) {
2407 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2408 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2409 updateInterruptionFilterLocked();
2412 Binder.restoreCallingIdentity(identity);
2417 public int getInterruptionFilterFromListener(INotificationListener token)
2418 throws RemoteException {
2419 synchronized (mNotificationLight) {
2420 return mInterruptionFilter;
2425 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2426 throws RemoteException {
2427 synchronized (mNotificationLock) {
2428 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2429 if (info == null) return;
2430 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2435 public int getZenMode() {
2436 return mZenModeHelper.getZenMode();
2440 public ZenModeConfig getZenModeConfig() {
2441 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2442 return mZenModeHelper.getConfig();
2446 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2447 enforceSystemOrSystemUI("INotificationManager.setZenMode");
2448 final long identity = Binder.clearCallingIdentity();
2450 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2452 Binder.restoreCallingIdentity(identity);
2457 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2458 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2459 return mZenModeHelper.getZenRules();
2463 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2464 Preconditions.checkNotNull(id, "Id is null");
2465 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2466 return mZenModeHelper.getAutomaticZenRule(id);
2470 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2471 throws RemoteException {
2472 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2473 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2474 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2475 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2476 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2478 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2479 "addAutomaticZenRule");
2483 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2484 throws RemoteException {
2485 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2486 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2487 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2488 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2489 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2491 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2492 "updateAutomaticZenRule");
2496 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2497 Preconditions.checkNotNull(id, "Id is null");
2498 // Verify that they can modify zen rules.
2499 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2501 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2505 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2506 Preconditions.checkNotNull(packageName, "Package name is null");
2507 enforceSystemOrSystemUI("removeAutomaticZenRules");
2509 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2513 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2514 Preconditions.checkNotNull(owner, "Owner is null");
2515 enforceSystemOrSystemUI("getRuleInstanceCount");
2517 return mZenModeHelper.getCurrentInstanceCount(owner);
2521 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2522 enforcePolicyAccess(pkg, "setInterruptionFilter");
2523 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2524 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2525 final long identity = Binder.clearCallingIdentity();
2527 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2529 Binder.restoreCallingIdentity(identity);
2534 public void notifyConditions(final String pkg, IConditionProvider provider,
2535 final Condition[] conditions) {
2536 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2537 checkCallerIsSystemOrSameApp(pkg);
2538 mHandler.post(new Runnable() {
2541 mConditionProviders.notifyConditions(pkg, info, conditions);
2547 public void requestUnbindProvider(IConditionProvider provider) {
2548 long identity = Binder.clearCallingIdentity();
2550 // allow bound services to disable themselves
2551 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2552 info.getOwner().setComponentState(info.component, false);
2554 Binder.restoreCallingIdentity(identity);
2559 public void requestBindProvider(ComponentName component) {
2560 checkCallerIsSystemOrSameApp(component.getPackageName());
2561 long identity = Binder.clearCallingIdentity();
2563 mConditionProviders.setComponentState(component, true);
2565 Binder.restoreCallingIdentity(identity);
2569 private void enforceSystemOrSystemUI(String message) {
2570 if (isCallerSystemOrPhone()) return;
2571 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2575 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2577 checkCallerIsSystemOrSameApp(pkg);
2578 } catch (SecurityException e) {
2579 getContext().enforceCallingPermission(
2580 android.Manifest.permission.STATUS_BAR_SERVICE,
2585 private void enforcePolicyAccess(int uid, String method) {
2586 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2587 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2590 boolean accessAllowed = false;
2591 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2592 final int packageCount = packages.length;
2593 for (int i = 0; i < packageCount; i++) {
2594 if (mConditionProviders.isPackageOrComponentAllowed(
2595 packages[i], UserHandle.getUserId(uid))) {
2596 accessAllowed = true;
2599 if (!accessAllowed) {
2600 Slog.w(TAG, "Notification policy access denied calling " + method);
2601 throw new SecurityException("Notification policy access denied");
2605 private void enforcePolicyAccess(String pkg, String method) {
2606 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2607 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2610 checkCallerIsSameApp(pkg);
2611 if (!checkPolicyAccess(pkg)) {
2612 Slog.w(TAG, "Notification policy access denied calling " + method);
2613 throw new SecurityException("Notification policy access denied");
2617 private boolean checkPackagePolicyAccess(String pkg) {
2618 return mConditionProviders.isPackageOrComponentAllowed(
2619 pkg, getCallingUserHandle().getIdentifier());
2622 private boolean checkPolicyAccess(String pkg) {
2624 int uid = getContext().getPackageManager().getPackageUidAsUser(
2625 pkg, UserHandle.getCallingUserId());
2626 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2627 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2631 } catch (NameNotFoundException e) {
2634 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2638 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2639 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2640 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2641 if (filter != null && filter.stats) {
2642 dumpJson(pw, filter);
2643 } else if (filter != null && filter.proto) {
2644 dumpProto(fd, filter);
2646 dumpImpl(pw, filter);
2651 public ComponentName getEffectsSuppressor() {
2652 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2656 public boolean matchesCallFilter(Bundle extras) {
2657 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2658 return mZenModeHelper.matchesCallFilter(
2659 Binder.getCallingUserHandle(),
2661 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2662 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2663 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2667 public boolean isSystemConditionProviderEnabled(String path) {
2668 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2669 return mConditionProviders.isSystemProviderEnabled(path);
2672 // Backup/restore interface
2674 public byte[] getBackupPayload(int user) {
2675 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2676 //TODO: http://b/22388012
2677 if (user != UserHandle.USER_SYSTEM) {
2678 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2681 synchronized(mPolicyFile) {
2682 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2684 writePolicyXml(baos, true /*forBackup*/);
2685 return baos.toByteArray();
2686 } catch (IOException e) {
2687 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2694 public void applyRestore(byte[] payload, int user) {
2695 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2696 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2697 if (payload == null) {
2698 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2701 //TODO: http://b/22388012
2702 if (user != UserHandle.USER_SYSTEM) {
2703 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2706 synchronized(mPolicyFile) {
2707 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2709 readPolicyXml(bais, true /*forRestore*/);
2711 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2712 Slog.w(TAG, "applyRestore: error reading payload", e);
2718 public boolean isNotificationPolicyAccessGranted(String pkg) {
2719 return checkPolicyAccess(pkg);
2723 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2724 enforceSystemOrSystemUIOrSamePackage(pkg,
2725 "request policy access status for another package");
2726 return checkPolicyAccess(pkg);
2730 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2731 throws RemoteException {
2732 checkCallerIsSystemOrShell();
2733 if (!mActivityManager.isLowRamDevice()) {
2734 mConditionProviders.setPackageOrComponentEnabled(
2735 pkg, getCallingUserHandle().getIdentifier(), true, granted);
2737 getContext().sendBroadcastAsUser(new Intent(
2738 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2740 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2741 getCallingUserHandle(), null);
2748 public Policy getNotificationPolicy(String pkg) {
2749 enforcePolicyAccess(pkg, "getNotificationPolicy");
2750 final long identity = Binder.clearCallingIdentity();
2752 return mZenModeHelper.getNotificationPolicy();
2754 Binder.restoreCallingIdentity(identity);
2759 public void setNotificationPolicy(String pkg, Policy policy) {
2760 enforcePolicyAccess(pkg, "setNotificationPolicy");
2761 final long identity = Binder.clearCallingIdentity();
2763 mZenModeHelper.setNotificationPolicy(policy);
2765 Binder.restoreCallingIdentity(identity);
2770 public List<String> getEnabledNotificationListenerPackages() {
2771 checkCallerIsSystem();
2772 return mListeners.getAllowedPackages(getCallingUserHandle().getIdentifier());
2776 public List<ComponentName> getEnabledNotificationListeners(int userId) {
2777 checkCallerIsSystem();
2778 return mListeners.getAllowedComponents(userId);
2782 public boolean isNotificationListenerAccessGranted(ComponentName listener) {
2783 Preconditions.checkNotNull(listener);
2784 checkCallerIsSystemOrSameApp(listener.getPackageName());
2785 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2786 getCallingUserHandle().getIdentifier());
2790 public boolean isNotificationListenerAccessGrantedForUser(ComponentName listener,
2792 Preconditions.checkNotNull(listener);
2793 checkCallerIsSystem();
2794 return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
2799 public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
2800 Preconditions.checkNotNull(assistant);
2801 checkCallerIsSystemOrSameApp(assistant.getPackageName());
2802 return mAssistants.isPackageOrComponentAllowed(assistant.flattenToString(),
2803 getCallingUserHandle().getIdentifier());
2807 public void setNotificationListenerAccessGranted(ComponentName listener,
2808 boolean granted) throws RemoteException {
2809 setNotificationListenerAccessGrantedForUser(
2810 listener, getCallingUserHandle().getIdentifier(), granted);
2814 public void setNotificationAssistantAccessGranted(ComponentName assistant,
2815 boolean granted) throws RemoteException {
2816 setNotificationAssistantAccessGrantedForUser(
2817 assistant, getCallingUserHandle().getIdentifier(), granted);
2821 public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
2822 boolean granted) throws RemoteException {
2823 Preconditions.checkNotNull(listener);
2824 checkCallerIsSystemOrShell();
2825 if (!mActivityManager.isLowRamDevice()) {
2826 mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
2827 userId, false, granted);
2828 mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
2829 userId, true, granted);
2831 getContext().sendBroadcastAsUser(new Intent(
2832 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2833 .setPackage(listener.getPackageName())
2834 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2835 getCallingUserHandle(), null);
2842 public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
2843 int userId, boolean granted) throws RemoteException {
2844 Preconditions.checkNotNull(assistant);
2845 checkCallerIsSystemOrShell();
2846 if (!mActivityManager.isLowRamDevice()) {
2847 mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
2848 userId, false, granted);
2849 mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
2850 userId, true, granted);
2852 getContext().sendBroadcastAsUser(new Intent(
2853 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
2854 .setPackage(assistant.getPackageName())
2855 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
2856 getCallingUserHandle(), null);
2863 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2864 Adjustment adjustment) throws RemoteException {
2865 final long identity = Binder.clearCallingIdentity();
2867 synchronized (mNotificationLock) {
2868 mAssistants.checkServiceTokenLocked(token);
2869 int N = mEnqueuedNotifications.size();
2870 for (int i = 0; i < N; i++) {
2871 final NotificationRecord n = mEnqueuedNotifications.get(i);
2872 if (Objects.equals(adjustment.getKey(), n.getKey())
2873 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2874 applyAdjustment(n, adjustment);
2880 Binder.restoreCallingIdentity(identity);
2885 public void applyAdjustmentFromAssistant(INotificationListener token,
2886 Adjustment adjustment) throws RemoteException {
2887 final long identity = Binder.clearCallingIdentity();
2889 synchronized (mNotificationLock) {
2890 mAssistants.checkServiceTokenLocked(token);
2891 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2892 applyAdjustment(n, adjustment);
2894 mRankingHandler.requestSort();
2896 Binder.restoreCallingIdentity(identity);
2901 public void applyAdjustmentsFromAssistant(INotificationListener token,
2902 List<Adjustment> adjustments) throws RemoteException {
2904 final long identity = Binder.clearCallingIdentity();
2906 synchronized (mNotificationLock) {
2907 mAssistants.checkServiceTokenLocked(token);
2908 for (Adjustment adjustment : adjustments) {
2909 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2910 applyAdjustment(n, adjustment);
2913 mRankingHandler.requestSort();
2915 Binder.restoreCallingIdentity(identity);
2920 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2921 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2922 Preconditions.checkNotNull(channel);
2923 Preconditions.checkNotNull(pkg);
2924 Preconditions.checkNotNull(user);
2926 verifyPrivilegedListener(token, user);
2927 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2931 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2932 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2933 Preconditions.checkNotNull(pkg);
2934 Preconditions.checkNotNull(user);
2935 verifyPrivilegedListener(token, user);
2937 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2938 false /* includeDeleted */);
2942 public ParceledListSlice<NotificationChannelGroup>
2943 getNotificationChannelGroupsFromPrivilegedListener(
2944 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2945 Preconditions.checkNotNull(pkg);
2946 Preconditions.checkNotNull(user);
2947 verifyPrivilegedListener(token, user);
2949 List<NotificationChannelGroup> groups = new ArrayList<>();
2950 groups.addAll(mRankingHelper.getNotificationChannelGroups(
2951 pkg, getUidForPackageAndUser(pkg, user)));
2952 return new ParceledListSlice<>(groups);
2955 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
2956 ManagedServiceInfo info;
2957 synchronized (mNotificationLock) {
2958 info = mListeners.checkServiceTokenLocked(token);
2960 if (!hasCompanionDevice(info)) {
2961 throw new SecurityException(info + " does not have access");
2963 if (!info.enabledAndUserMatches(user.getIdentifier())) {
2964 throw new SecurityException(info + " does not have access");
2968 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2970 long identity = Binder.clearCallingIdentity();
2972 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2974 Binder.restoreCallingIdentity(identity);
2980 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2981 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
2982 throws RemoteException {
2983 new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
2987 private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
2991 if (adjustment.getSignals() != null) {
2992 Bundle.setDefusable(adjustment.getSignals(), true);
2993 r.addAdjustment(adjustment);
2997 @GuardedBy("mNotificationLock")
2998 void addAutogroupKeyLocked(String key) {
2999 NotificationRecord r = mNotificationsByKey.get(key);
3003 if (r.sbn.getOverrideGroupKey() == null) {
3004 addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
3005 EventLogTags.writeNotificationAutogrouped(key);
3006 mRankingHandler.requestSort();
3010 @GuardedBy("mNotificationLock")
3011 void removeAutogroupKeyLocked(String key) {
3012 NotificationRecord r = mNotificationsByKey.get(key);
3016 if (r.sbn.getOverrideGroupKey() != null) {
3017 addAutoGroupAdjustment(r, null);
3018 EventLogTags.writeNotificationUnautogrouped(key);
3019 mRankingHandler.requestSort();
3023 private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
3024 Bundle signals = new Bundle();
3025 signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
3026 Adjustment adjustment =
3027 new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
3028 r.addAdjustment(adjustment);
3031 // Clears the 'fake' auto-group summary.
3032 @GuardedBy("mNotificationLock")
3033 private void clearAutogroupSummaryLocked(int userId, String pkg) {
3034 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3035 if (summaries != null && summaries.containsKey(pkg)) {
3037 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
3038 if (removed != null) {
3039 boolean wasPosted = removeFromNotificationListsLocked(removed);
3040 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted, null);
3045 @GuardedBy("mNotificationLock")
3046 private boolean hasAutoGroupSummaryLocked(StatusBarNotification sbn) {
3047 ArrayMap<String, String> summaries = mAutobundledSummaries.get(sbn.getUserId());
3048 return summaries != null && summaries.containsKey(sbn.getPackageName());
3051 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
3052 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
3053 NotificationRecord summaryRecord = null;
3054 synchronized (mNotificationLock) {
3055 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
3056 if (notificationRecord == null) {
3057 // The notification could have been cancelled again already. A successive
3058 // adjustment will post a summary if needed.
3061 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
3062 userId = adjustedSbn.getUser().getIdentifier();
3063 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
3064 if (summaries == null) {
3065 summaries = new ArrayMap<>();
3067 mAutobundledSummaries.put(userId, summaries);
3068 if (!summaries.containsKey(pkg)) {
3070 final ApplicationInfo appInfo =
3071 adjustedSbn.getNotification().extras.getParcelable(
3072 Notification.EXTRA_BUILDER_APPLICATION_INFO);
3073 final Bundle extras = new Bundle();
3074 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
3075 final String channelId = notificationRecord.getChannel().getId();
3076 final Notification summaryNotification =
3077 new Notification.Builder(getContext(), channelId)
3078 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
3079 .setGroupSummary(true)
3080 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
3081 .setGroup(GroupHelper.AUTOGROUP_KEY)
3082 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
3083 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
3084 .setColor(adjustedSbn.getNotification().color)
3087 summaryNotification.extras.putAll(extras);
3088 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
3089 if (appIntent != null) {
3090 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
3091 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
3093 final StatusBarNotification summarySbn =
3094 new StatusBarNotification(adjustedSbn.getPackageName(),
3095 adjustedSbn.getOpPkg(),
3097 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
3098 adjustedSbn.getInitialPid(), summaryNotification,
3099 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
3100 System.currentTimeMillis());
3101 summaryRecord = new NotificationRecord(getContext(), summarySbn,
3102 notificationRecord.getChannel());
3103 summaries.put(pkg, summarySbn.getKey());
3106 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
3107 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
3108 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
3112 private String disableNotificationEffects(NotificationRecord record) {
3113 if (mDisableNotificationEffects) {
3114 return "booleanState";
3116 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
3117 return "listenerHints";
3119 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
3125 private void dumpJson(PrintWriter pw, DumpFilter filter) {
3126 JSONObject dump = new JSONObject();
3128 dump.put("service", "Notification Manager");
3129 dump.put("bans", mRankingHelper.dumpBansJson(filter));
3130 dump.put("ranking", mRankingHelper.dumpJson(filter));
3131 dump.put("stats", mUsageStats.dumpJson(filter));
3132 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
3133 } catch (JSONException e) {
3134 e.printStackTrace();
3139 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
3140 final ProtoOutputStream proto = new ProtoOutputStream(fd);
3141 synchronized (mNotificationLock) {
3142 long records = proto.start(NotificationServiceDumpProto.RECORDS);
3143 int N = mNotificationList.size();
3145 for (int i = 0; i < N; i++) {
3146 final NotificationRecord nr = mNotificationList.get(i);
3147 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3148 nr.dump(proto, filter.redact);
3149 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
3152 N = mEnqueuedNotifications.size();
3154 for (int i = 0; i < N; i++) {
3155 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3156 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3157 nr.dump(proto, filter.redact);
3158 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
3161 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
3164 for (int i = 0; i < N; i++) {
3165 final NotificationRecord nr = snoozed.get(i);
3166 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3167 nr.dump(proto, filter.redact);
3168 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
3174 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
3175 mZenModeHelper.dump(proto);
3176 for (ComponentName suppressor : mEffectsSuppressors) {
3177 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
3184 void dumpImpl(PrintWriter pw, DumpFilter filter) {
3185 pw.print("Current Notification Manager state");
3186 if (filter.filtered) {
3187 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
3191 final boolean zenOnly = filter.filtered && filter.zen;
3194 synchronized (mToastQueue) {
3195 N = mToastQueue.size();
3197 pw.println(" Toast Queue:");
3198 for (int i=0; i<N; i++) {
3199 mToastQueue.get(i).dump(pw, " ", filter);
3206 synchronized (mNotificationLock) {
3208 N = mNotificationList.size();
3210 pw.println(" Notification List:");
3211 for (int i=0; i<N; i++) {
3212 final NotificationRecord nr = mNotificationList.get(i);
3213 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3214 nr.dump(pw, " ", getContext(), filter.redact);
3219 if (!filter.filtered) {
3222 pw.println(" Lights List:");
3223 for (int i=0; i<N; i++) {
3229 pw.println(mLights.get(i));
3233 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3234 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
3235 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3236 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
3237 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
3238 pw.println(" mCallState=" + callStateToString(mCallState));
3239 pw.println(" mSystemReady=" + mSystemReady);
3240 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
3242 pw.println(" mArchive=" + mArchive.toString());
3243 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
3245 while (iter.hasNext()) {
3246 final StatusBarNotification sbn = iter.next();
3247 if (filter != null && !filter.matches(sbn)) continue;
3248 pw.println(" " + sbn);
3250 if (iter.hasNext()) pw.println(" ...");
3256 N = mEnqueuedNotifications.size();
3258 pw.println(" Enqueued Notification List:");
3259 for (int i = 0; i < N; i++) {
3260 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3261 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3262 nr.dump(pw, " ", getContext(), filter.redact);
3267 mSnoozeHelper.dump(pw, filter);
3272 pw.println("\n Ranking Config:");
3273 mRankingHelper.dump(pw, " ", filter);
3275 pw.println("\n Notification listeners:");
3276 mListeners.dump(pw, filter);
3277 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3278 pw.print(" mListenersDisablingEffects: (");
3279 N = mListenersDisablingEffects.size();
3280 for (int i = 0; i < N; i++) {
3281 final int hint = mListenersDisablingEffects.keyAt(i);
3282 if (i > 0) pw.print(';');
3283 pw.print("hint[" + hint + "]:");
3285 final ArraySet<ManagedServiceInfo> listeners =
3286 mListenersDisablingEffects.valueAt(i);
3287 final int listenerSize = listeners.size();
3289 for (int j = 0; j < listenerSize; j++) {
3290 if (i > 0) pw.print(',');
3291 final ManagedServiceInfo listener = listeners.valueAt(i);
3292 pw.print(listener.component);
3296 pw.println("\n Notification assistant services:");
3297 mAssistants.dump(pw, filter);
3300 if (!filter.filtered || zenOnly) {
3301 pw.println("\n Zen Mode:");
3302 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3303 mZenModeHelper.dump(pw, " ");
3305 pw.println("\n Zen Log:");
3306 ZenLog.dump(pw, " ");
3309 pw.println("\n Condition providers:");
3310 mConditionProviders.dump(pw, filter);
3312 pw.println("\n Group summaries:");
3313 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3314 NotificationRecord r = entry.getValue();
3315 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3316 if (mNotificationsByKey.get(r.getKey()) != r) {
3317 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3318 r.dump(pw, " ", getContext(), filter.redact);
3323 pw.println("\n Usage Stats:");
3324 mUsageStats.dump(pw, " ", filter);
3330 * The private API only accessible to the system process.
3332 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3334 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3335 String tag, int id, Notification notification, int userId) {
3336 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3341 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3343 checkCallerIsSystem();
3344 mHandler.post(new Runnable() {
3347 synchronized (mNotificationLock) {
3348 removeForegroundServiceFlagByListLocked(
3349 mEnqueuedNotifications, pkg, notificationId, userId);
3350 removeForegroundServiceFlagByListLocked(
3351 mNotificationList, pkg, notificationId, userId);
3357 @GuardedBy("mNotificationLock")
3358 private void removeForegroundServiceFlagByListLocked(
3359 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3361 NotificationRecord r = findNotificationByListLocked(
3362 notificationList, pkg, null, notificationId, userId);
3366 StatusBarNotification sbn = r.sbn;
3367 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3368 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3369 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3370 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3371 sbn.getNotification().flags =
3372 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3373 mRankingHelper.sort(mNotificationList);
3374 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3378 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3379 final int callingPid, final String tag, final int id, final Notification notification,
3380 int incomingUserId) {
3382 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3383 + " notification=" + notification);
3385 checkCallerIsSystemOrSameApp(pkg);
3387 final int userId = ActivityManager.handleIncomingUser(callingPid,
3388 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3389 final UserHandle user = new UserHandle(userId);
3391 if (pkg == null || notification == null) {
3392 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3393 + " id=" + id + " notification=" + notification);
3396 // The system can post notifications for any package, let us resolve that.
3397 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3399 // Fix the notification as best we can.
3401 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3402 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3403 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3404 Notification.addFieldsFromContext(ai, notification);
3406 int canColorize = mPackageManagerClient.checkPermission(
3407 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
3408 if (canColorize == PERMISSION_GRANTED) {
3409 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3411 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3414 } catch (NameNotFoundException e) {
3415 Slog.e(TAG, "Cannot create a context for sending app", e);
3419 mUsageStats.registerEnqueuedByApp(pkg);
3421 // setup local book-keeping
3422 String channelId = notification.getChannelId();
3423 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3424 channelId = (new Notification.TvExtender(notification)).getChannelId();
3426 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3427 notificationUid, channelId, false /* includeDeleted */);
3428 if (channel == null) {
3429 final String noChannelStr = "No Channel found for "
3431 + ", channelId=" + channelId
3434 + ", opPkg=" + opPkg
3435 + ", callingUid=" + callingUid
3436 + ", userId=" + userId
3437 + ", incomingUserId=" + incomingUserId
3438 + ", notificationUid=" + notificationUid
3439 + ", notification=" + notification;
3440 Log.e(TAG, noChannelStr);
3441 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3442 "Failed to post notification on channel \"" + channelId + "\"\n" +
3443 "See log for more details");
3447 final StatusBarNotification n = new StatusBarNotification(
3448 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3449 user, null, System.currentTimeMillis());
3450 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3452 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
3456 // Whitelist pending intents.
3457 if (notification.allPendingIntents != null) {
3458 final int intentCount = notification.allPendingIntents.size();
3459 if (intentCount > 0) {
3460 final ActivityManagerInternal am = LocalServices
3461 .getService(ActivityManagerInternal.class);
3462 final long duration = LocalServices.getService(
3463 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3464 for (int i = 0; i < intentCount; i++) {
3465 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3466 if (pendingIntent != null) {
3467 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3468 WHITELIST_TOKEN, duration);
3474 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3477 private void doChannelWarningToast(CharSequence toastText) {
3478 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3479 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3480 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3481 if (warningEnabled) {
3482 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3483 Toast.LENGTH_SHORT);
3488 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3489 // The system can post notifications on behalf of any package it wants
3490 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3492 return getContext().getPackageManager()
3493 .getPackageUidAsUser(opPackageName, userId);
3494 } catch (NameNotFoundException e) {
3502 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3506 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3507 NotificationRecord r) {
3508 final String pkg = r.sbn.getPackageName();
3509 final String dialerPackage =
3510 getContext().getSystemService(TelecomManager.class).getSystemDialerPackage();
3511 final boolean isSystemNotification =
3512 isUidSystemOrPhone(callingUid) || ("android".equals(pkg))
3513 || TextUtils.equals(pkg, dialerPackage);
3514 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3516 // Limit the number of notifications that any given package except the android
3517 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3518 if (!isSystemNotification && !isNotificationFromListener) {
3519 synchronized (mNotificationLock) {
3520 if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
3521 // Ephemeral apps have some special constraints for notifications.
3522 // They are not allowed to create new notifications however they are allowed to
3523 // update notifications created by the system (e.g. a foreground service
3525 throw new SecurityException("Instant app " + pkg
3526 + " cannot create notifications");
3529 // rate limit updates that aren't completed progress notifications
3530 if (mNotificationsByKey.get(r.sbn.getKey()) != null
3531 && !r.getNotification().hasCompletedProgress()) {
3533 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3534 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3535 mUsageStats.registerOverRateQuota(pkg);
3536 final long now = SystemClock.elapsedRealtime();
3537 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3538 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3539 + ". Shedding events. package=" + pkg);
3540 mLastOverRateLogTime = now;
3546 // limit the number of outstanding notificationrecords an app can have
3547 int count = getNotificationCountLocked(pkg, userId, id, tag);
3548 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3549 mUsageStats.registerOverCountQuota(pkg);
3550 Slog.e(TAG, "Package has already posted or enqueued " + count
3551 + " notifications. Not showing more. package=" + pkg);
3558 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3559 MetricsLogger.action(r.getLogMaker()
3560 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3561 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3563 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3565 mSnoozeHelper.update(userId, r);
3572 if (isBlocked(r, mUsageStats)) {
3579 protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
3580 String excludedTag) {
3582 final int N = mNotificationList.size();
3583 for (int i = 0; i < N; i++) {
3584 final NotificationRecord existing = mNotificationList.get(i);
3585 if (existing.sbn.getPackageName().equals(pkg)
3586 && existing.sbn.getUserId() == userId) {
3587 if (existing.sbn.getId() == excludedId
3588 && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
3594 final int M = mEnqueuedNotifications.size();
3595 for (int i = 0; i < M; i++) {
3596 final NotificationRecord existing = mEnqueuedNotifications.get(i);
3597 if (existing.sbn.getPackageName().equals(pkg)
3598 && existing.sbn.getUserId() == userId) {
3605 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3606 final String pkg = r.sbn.getPackageName();
3607 final int callingUid = r.sbn.getUid();
3609 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3610 if (isPackageSuspended) {
3611 Slog.e(TAG, "Suppressing notification from package due to package "
3612 + "suspended by administrator.");
3613 usageStats.registerSuspendedByAdmin(r);
3614 return isPackageSuspended;
3617 final boolean isBlocked =
3618 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
3619 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3621 Slog.e(TAG, "Suppressing notification from package by user request.");
3622 usageStats.registerBlocked(r);
3627 protected class SnoozeNotificationRunnable implements Runnable {
3628 private final String mKey;
3629 private final long mDuration;
3630 private final String mSnoozeCriterionId;
3632 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3634 mDuration = duration;
3635 mSnoozeCriterionId = snoozeCriterionId;
3640 synchronized (mNotificationLock) {
3641 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3648 @GuardedBy("mNotificationLock")
3649 void snoozeLocked(NotificationRecord r) {
3650 if (r.sbn.isGroup()) {
3651 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3652 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3653 if (r.getNotification().isGroupSummary()) {
3654 // snooze summary and all children
3655 for (int i = 0; i < groupNotifications.size(); i++) {
3656 snoozeNotificationLocked(groupNotifications.get(i));
3659 // if there is a valid summary for this group, and we are snoozing the only
3660 // child, also snooze the summary
3661 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3662 if (groupNotifications.size() != 2) {
3663 snoozeNotificationLocked(r);
3665 // snooze summary and the one child
3666 for (int i = 0; i < groupNotifications.size(); i++) {
3667 snoozeNotificationLocked(groupNotifications.get(i));
3671 snoozeNotificationLocked(r);
3675 // just snooze the one notification
3676 snoozeNotificationLocked(r);
3680 @GuardedBy("mNotificationLock")
3681 void snoozeNotificationLocked(NotificationRecord r) {
3682 MetricsLogger.action(r.getLogMaker()
3683 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3684 .setType(MetricsEvent.TYPE_CLOSE)
3685 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3686 mSnoozeCriterionId == null ? 0 : 1));
3687 boolean wasPosted = removeFromNotificationListsLocked(r);
3688 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
3689 updateLightsLocked();
3690 if (mSnoozeCriterionId != null) {
3691 mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3692 mSnoozeHelper.snooze(r);
3694 mSnoozeHelper.snooze(r, mDuration);
3700 protected class EnqueueNotificationRunnable implements Runnable {
3701 private final NotificationRecord r;
3702 private final int userId;
3704 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3705 this.userId = userId;
3711 synchronized (mNotificationLock) {
3712 mEnqueuedNotifications.add(r);
3713 scheduleTimeoutLocked(r);
3715 final StatusBarNotification n = r.sbn;
3716 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3717 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3719 // Retain ranking information from previous record
3720 r.copyRankingInformation(old);
3723 final int callingUid = n.getUid();
3724 final int callingPid = n.getInitialPid();
3725 final Notification notification = n.getNotification();
3726 final String pkg = n.getPackageName();
3727 final int id = n.getId();
3728 final String tag = n.getTag();
3730 // Handle grouped notifications and bail out early if we
3731 // can to avoid extracting signals.
3732 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3734 // if this is a group child, unsnooze parent summary
3735 if (n.isGroup() && notification.isGroupChild()) {
3736 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3739 // This conditional is a dirty hack to limit the logging done on
3740 // behalf of the download manager without affecting other apps.
3741 if (!pkg.equals("com.android.providers.downloads")
3742 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3743 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3745 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3747 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3748 pkg, id, tag, userId, notification.toString(),
3752 mRankingHelper.extractSignals(r);
3754 // tell the assistant service about the notification
3755 if (mAssistants.isEnabled()) {
3756 mAssistants.onNotificationEnqueued(r);
3757 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3758 DELAY_FOR_ASSISTANT_TIME);
3760 mHandler.post(new PostNotificationRunnable(r.getKey()));
3766 protected class PostNotificationRunnable implements Runnable {
3767 private final String key;
3769 PostNotificationRunnable(String key) {
3775 synchronized (mNotificationLock) {
3777 NotificationRecord r = null;
3778 int N = mEnqueuedNotifications.size();
3779 for (int i = 0; i < N; i++) {
3780 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3781 if (Objects.equals(key, enqueued.getKey())) {
3787 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3790 NotificationRecord old = mNotificationsByKey.get(key);
3791 final StatusBarNotification n = r.sbn;
3792 final Notification notification = n.getNotification();
3793 int index = indexOfNotificationLocked(n.getKey());
3795 mNotificationList.add(r);
3796 mUsageStats.registerPostedByApp(r);
3798 old = mNotificationList.get(index);
3799 mNotificationList.set(index, r);
3800 mUsageStats.registerUpdatedByApp(r, old);
3801 // Make sure we don't lose the foreground service state.
3802 notification.flags |=
3803 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3807 mNotificationsByKey.put(n.getKey(), r);
3809 // Ensure if this is a foreground service that the proper additional
3811 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3812 notification.flags |= Notification.FLAG_ONGOING_EVENT
3813 | Notification.FLAG_NO_CLEAR;
3816 applyZenModeLocked(r);
3817 mRankingHelper.sort(mNotificationList);
3819 if (notification.getSmallIcon() != null) {
3820 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3821 mListeners.notifyPostedLocked(n, oldSbn);
3822 if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
3823 mHandler.post(new Runnable() {
3826 mGroupHelper.onNotificationPosted(
3827 n, hasAutoGroupSummaryLocked(n));
3832 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3833 if (old != null && !old.isCanceled) {
3834 mListeners.notifyRemovedLocked(n,
3835 NotificationListenerService.REASON_ERROR);
3836 mHandler.post(new Runnable() {
3839 mGroupHelper.onNotificationRemoved(n);
3843 // ATTENTION: in a future release we will bail out here
3844 // so that we do not play sounds, show lights, etc. for invalid
3846 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3847 + n.getPackageName());
3850 buzzBeepBlinkLocked(r);
3852 int N = mEnqueuedNotifications.size();
3853 for (int i = 0; i < N; i++) {
3854 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3855 if (Objects.equals(key, enqueued.getKey())) {
3856 mEnqueuedNotifications.remove(i);
3866 * Ensures that grouped notification receive their special treatment.
3868 * <p>Cancels group children if the new notification causes a group to lose
3871 * <p>Updates mSummaryByGroupKey.</p>
3873 @GuardedBy("mNotificationLock")
3874 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3875 int callingUid, int callingPid) {
3876 StatusBarNotification sbn = r.sbn;
3877 Notification n = sbn.getNotification();
3878 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3879 // notifications without a group shouldn't be a summary, otherwise autobundling can
3881 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3884 String group = sbn.getGroupKey();
3885 boolean isSummary = n.isGroupSummary();
3887 Notification oldN = old != null ? old.sbn.getNotification() : null;
3888 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3889 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3892 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3893 if (removedSummary != old) {
3895 removedSummary != null ? removedSummary.getKey() : "<null>";
3896 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3897 ", removed=" + removedKey);
3901 mSummaryByGroupKey.put(group, r);
3904 // Clear out group children of the old notification if the update
3905 // causes the group summary to go away. This happens when the old
3906 // notification was a summary and the new one isn't, or when the old
3907 // notification was a summary and its group key changed.
3908 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3909 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
3915 @GuardedBy("mNotificationLock")
3916 void scheduleTimeoutLocked(NotificationRecord record) {
3917 if (record.getNotification().getTimeoutAfter() > 0) {
3918 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3919 REQUEST_CODE_TIMEOUT,
3920 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3921 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3922 .appendPath(record.getKey()).build())
3923 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3924 .putExtra(EXTRA_KEY, record.getKey()),
3925 PendingIntent.FLAG_UPDATE_CURRENT);
3926 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3927 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
3932 @GuardedBy("mNotificationLock")
3933 void buzzBeepBlinkLocked(NotificationRecord record) {
3934 boolean buzz = false;
3935 boolean beep = false;
3936 boolean blink = false;
3938 final Notification notification = record.sbn.getNotification();
3939 final String key = record.getKey();
3941 // Should this notification make noise, vibe, or use the LED?
3942 final boolean aboveThreshold =
3943 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
3945 // Remember if this notification already owns the notification channels.
3946 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3947 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
3948 // These are set inside the conditional if the notification is allowed to make noise.
3949 boolean hasValidVibrate = false;
3950 boolean hasValidSound = false;
3952 if (aboveThreshold && isNotificationForCurrentUser(record)) {
3953 // If the notification will appear in the status bar, it should send an accessibility
3955 if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
3956 sendAccessibilityEvent(notification, record.sbn.getPackageName());
3958 if (mSystemReady && mAudioManager != null) {
3959 Uri soundUri = record.getSound();
3960 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
3962 long[] vibration = record.getVibration();
3963 // Demote sound to vibration if vibration missing & phone in vibration mode.
3964 if (vibration == null
3966 && (mAudioManager.getRingerModeInternal()
3967 == AudioManager.RINGER_MODE_VIBRATE)) {
3968 vibration = mFallbackVibrationPattern;
3970 hasValidVibrate = vibration != null;
3972 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
3974 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
3975 if (DBG) Slog.v(TAG, "Interrupting!");
3976 if (hasValidSound) {
3977 mSoundNotificationKey = key;
3979 playInCallNotification();
3982 beep = playSound(record, soundUri);
3986 final boolean ringerModeSilent =
3987 mAudioManager.getRingerModeInternal()
3988 == AudioManager.RINGER_MODE_SILENT;
3989 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
3990 mVibrateNotificationKey = key;
3992 buzz = playVibration(record, vibration, hasValidSound);
3997 // If a notification is updated to remove the actively playing sound or vibrate,
3998 // cancel that feedback now
3999 if (wasBeep && !hasValidSound) {
4002 if (wasBuzz && !hasValidVibrate) {
4003 clearVibrateLocked();
4007 // release the light
4008 boolean wasShowLights = mLights.remove(key);
4009 if (record.getLight() != null && aboveThreshold
4010 && ((record.getSuppressedVisualEffects()
4011 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
4013 updateLightsLocked();
4014 if (mUseAttentionLight) {
4015 mAttentionLight.pulse();
4018 } else if (wasShowLights) {
4019 updateLightsLocked();
4021 if (buzz || beep || blink) {
4022 MetricsLogger.action(record.getLogMaker()
4023 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
4024 .setType(MetricsEvent.TYPE_OPEN)
4025 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
4026 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
4030 @GuardedBy("mNotificationLock")
4031 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
4032 // Suppressed because it's a silent update
4033 final Notification notification = record.getNotification();
4035 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
4039 // muted by listener
4040 final String disableEffects = disableNotificationEffects(record);
4041 if (disableEffects != null) {
4042 ZenLog.traceDisableEffects(record, disableEffects);
4046 // suppressed due to DND
4047 if (record.isIntercepted()) {
4051 // Suppressed because another notification in its group handles alerting
4052 if (record.sbn.isGroup()) {
4053 return notification.suppressAlertingDueToGrouping();
4056 // Suppressed for being too recently noisy
4057 final String pkg = record.sbn.getPackageName();
4058 if (mUsageStats.isAlertRateLimited(pkg)) {
4059 Slog.e(TAG, "Muting recently noisy " + record.getKey());
4066 private boolean playSound(final NotificationRecord record, Uri soundUri) {
4067 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4068 // do not play notifications if there is a user of exclusive audio focus
4069 // or the device is in vibrate mode
4070 if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
4071 != AudioManager.RINGER_MODE_VIBRATE) {
4072 final long identity = Binder.clearCallingIdentity();
4074 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4075 if (player != null) {
4076 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
4077 + " with attributes " + record.getAudioAttributes());
4078 player.playAsync(soundUri, record.sbn.getUser(), looping,
4079 record.getAudioAttributes());
4082 } catch (RemoteException e) {
4084 Binder.restoreCallingIdentity(identity);
4090 private boolean playVibration(final NotificationRecord record, long[] vibration,
4091 boolean delayVibForSound) {
4092 // Escalate privileges so we can use the vibrator even if the
4093 // notifying app does not have the VIBRATE permission.
4094 long identity = Binder.clearCallingIdentity();
4096 final VibrationEffect effect;
4098 final boolean insistent =
4099 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
4100 effect = VibrationEffect.createWaveform(
4101 vibration, insistent ? 0 : -1 /*repeatIndex*/);
4102 } catch (IllegalArgumentException e) {
4103 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
4104 Arrays.toString(vibration));
4107 if (delayVibForSound) {
4109 // delay the vibration by the same amount as the notification sound
4110 final int waitMs = mAudioManager.getFocusRampTimeMs(
4111 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
4112 record.getAudioAttributes());
4113 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
4115 Thread.sleep(waitMs);
4116 } catch (InterruptedException e) { }
4117 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4118 effect, record.getAudioAttributes());
4121 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
4122 effect, record.getAudioAttributes());
4126 Binder.restoreCallingIdentity(identity);
4130 private boolean isNotificationForCurrentUser(NotificationRecord record) {
4131 final int currentUser;
4132 final long token = Binder.clearCallingIdentity();
4134 currentUser = ActivityManager.getCurrentUser();
4136 Binder.restoreCallingIdentity(token);
4138 return (record.getUserId() == UserHandle.USER_ALL ||
4139 record.getUserId() == currentUser ||
4140 mUserProfiles.isCurrentProfile(record.getUserId()));
4143 protected void playInCallNotification() {
4147 final long identity = Binder.clearCallingIdentity();
4149 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4150 if (player != null) {
4151 player.play(new Binder(), mInCallNotificationUri,
4152 mInCallNotificationAudioAttributes,
4153 mInCallNotificationVolume, false);
4155 } catch (RemoteException e) {
4157 Binder.restoreCallingIdentity(identity);
4163 @GuardedBy("mToastQueue")
4164 void showNextToastLocked() {
4165 ToastRecord record = mToastQueue.get(0);
4166 while (record != null) {
4167 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
4169 record.callback.show(record.token);
4170 scheduleTimeoutLocked(record);
4172 } catch (RemoteException e) {
4173 Slog.w(TAG, "Object died trying to show notification " + record.callback
4174 + " in package " + record.pkg);
4175 // remove it from the list and let the process die
4176 int index = mToastQueue.indexOf(record);
4178 mToastQueue.remove(index);
4180 keepProcessAliveIfNeededLocked(record.pid);
4181 if (mToastQueue.size() > 0) {
4182 record = mToastQueue.get(0);
4190 @GuardedBy("mToastQueue")
4191 void cancelToastLocked(int index) {
4192 ToastRecord record = mToastQueue.get(index);
4194 record.callback.hide();
4195 } catch (RemoteException e) {
4196 Slog.w(TAG, "Object died trying to hide notification " + record.callback
4197 + " in package " + record.pkg);
4198 // don't worry about this, we're about to remove it from
4202 ToastRecord lastToast = mToastQueue.remove(index);
4203 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
4205 keepProcessAliveIfNeededLocked(record.pid);
4206 if (mToastQueue.size() > 0) {
4207 // Show the next one. If the callback fails, this will remove
4208 // it from the list, so don't assume that the list hasn't changed
4209 // after this point.
4210 showNextToastLocked();
4214 @GuardedBy("mToastQueue")
4215 private void scheduleTimeoutLocked(ToastRecord r)
4217 mHandler.removeCallbacksAndMessages(r);
4218 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
4219 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
4220 mHandler.sendMessageDelayed(m, delay);
4223 private void handleTimeout(ToastRecord record)
4225 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
4226 synchronized (mToastQueue) {
4227 int index = indexOfToastLocked(record.pkg, record.callback);
4229 cancelToastLocked(index);
4234 @GuardedBy("mToastQueue")
4235 int indexOfToastLocked(String pkg, ITransientNotification callback)
4237 IBinder cbak = callback.asBinder();
4238 ArrayList<ToastRecord> list = mToastQueue;
4239 int len = list.size();
4240 for (int i=0; i<len; i++) {
4241 ToastRecord r = list.get(i);
4242 if (r.pkg.equals(pkg) && r.callback.asBinder().equals(cbak)) {
4249 @GuardedBy("mToastQueue")
4250 int indexOfToastPackageLocked(String pkg)
4252 ArrayList<ToastRecord> list = mToastQueue;
4253 int len = list.size();
4254 for (int i=0; i<len; i++) {
4255 ToastRecord r = list.get(i);
4256 if (r.pkg.equals(pkg)) {
4263 @GuardedBy("mToastQueue")
4264 void keepProcessAliveIfNeededLocked(int pid)
4266 int toastCount = 0; // toasts from this pid
4267 ArrayList<ToastRecord> list = mToastQueue;
4268 int N = list.size();
4269 for (int i=0; i<N; i++) {
4270 ToastRecord r = list.get(i);
4276 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
4277 } catch (RemoteException e) {
4278 // Shouldn't happen.
4282 private void handleRankingReconsideration(Message message) {
4283 if (!(message.obj instanceof RankingReconsideration)) return;
4284 RankingReconsideration recon = (RankingReconsideration) message.obj;
4287 synchronized (mNotificationLock) {
4288 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4289 if (record == null) {
4292 int indexBefore = findNotificationRecordIndexLocked(record);
4293 boolean interceptBefore = record.isIntercepted();
4294 float contactAffinityBefore = record.getContactAffinity();
4295 int visibilityBefore = record.getPackageVisibilityOverride();
4296 recon.applyChangesLocked(record);
4297 applyZenModeLocked(record);
4298 mRankingHelper.sort(mNotificationList);
4299 int indexAfter = findNotificationRecordIndexLocked(record);
4300 boolean interceptAfter = record.isIntercepted();
4301 float contactAffinityAfter = record.getContactAffinity();
4302 int visibilityAfter = record.getPackageVisibilityOverride();
4303 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4304 || visibilityBefore != visibilityAfter;
4305 if (interceptBefore && !interceptAfter
4306 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
4307 buzzBeepBlinkLocked(record);
4311 mHandler.scheduleSendRankingUpdate();
4315 void handleRankingSort() {
4316 if (mRankingHelper == null) return;
4317 synchronized (mNotificationLock) {
4318 final int N = mNotificationList.size();
4319 // Any field that can change via one of the extractors needs to be added here.
4320 ArrayList<String> orderBefore = new ArrayList<>(N);
4321 int[] visibilities = new int[N];
4322 boolean[] showBadges = new boolean[N];
4323 ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
4324 ArrayList<String> groupKeyBefore = new ArrayList<>(N);
4325 ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
4326 ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
4327 for (int i = 0; i < N; i++) {
4328 final NotificationRecord r = mNotificationList.get(i);
4329 orderBefore.add(r.getKey());
4330 visibilities[i] = r.getPackageVisibilityOverride();
4331 showBadges[i] = r.canShowBadge();
4332 channelBefore.add(r.getChannel());
4333 groupKeyBefore.add(r.getGroupKey());
4334 overridePeopleBefore.add(r.getPeopleOverride());
4335 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
4336 mRankingHelper.extractSignals(r);
4338 mRankingHelper.sort(mNotificationList);
4339 for (int i = 0; i < N; i++) {
4340 final NotificationRecord r = mNotificationList.get(i);
4341 if (!orderBefore.get(i).equals(r.getKey())
4342 || visibilities[i] != r.getPackageVisibilityOverride()
4343 || showBadges[i] != r.canShowBadge()
4344 || !Objects.equals(channelBefore.get(i), r.getChannel())
4345 || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
4346 || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
4347 || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
4348 mHandler.scheduleSendRankingUpdate();
4355 @GuardedBy("mNotificationLock")
4356 private void recordCallerLocked(NotificationRecord record) {
4357 if (mZenModeHelper.isCall(record)) {
4358 mZenModeHelper.recordCaller(record);
4362 // let zen mode evaluate this record
4363 @GuardedBy("mNotificationLock")
4364 private void applyZenModeLocked(NotificationRecord record) {
4365 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4366 if (record.isIntercepted()) {
4367 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4368 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4369 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4370 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4371 record.setSuppressedVisualEffects(suppressed);
4373 record.setSuppressedVisualEffects(0);
4377 @GuardedBy("mNotificationLock")
4378 private int findNotificationRecordIndexLocked(NotificationRecord target) {
4379 return mRankingHelper.indexOf(mNotificationList, target);
4382 private void handleSendRankingUpdate() {
4383 synchronized (mNotificationLock) {
4384 mListeners.notifyRankingUpdateLocked();
4388 private void scheduleListenerHintsChanged(int state) {
4389 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4390 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4393 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4394 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4395 mHandler.obtainMessage(
4396 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4397 listenerInterruptionFilter,
4401 private void handleListenerHintsChanged(int hints) {
4402 synchronized (mNotificationLock) {
4403 mListeners.notifyListenerHintsChangedLocked(hints);
4407 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4408 synchronized (mNotificationLock) {
4409 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4413 protected class WorkerHandler extends Handler
4415 public WorkerHandler(Looper looper) {
4420 public void handleMessage(Message msg)
4424 case MESSAGE_TIMEOUT:
4425 handleTimeout((ToastRecord)msg.obj);
4427 case MESSAGE_SAVE_POLICY_FILE:
4428 handleSavePolicyFile();
4430 case MESSAGE_SEND_RANKING_UPDATE:
4431 handleSendRankingUpdate();
4433 case MESSAGE_LISTENER_HINTS_CHANGED:
4434 handleListenerHintsChanged(msg.arg1);
4436 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4437 handleListenerInterruptionFilterChanged(msg.arg1);
4442 protected void scheduleSendRankingUpdate() {
4443 if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4444 Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
4451 private final class RankingHandlerWorker extends Handler implements RankingHandler
4453 public RankingHandlerWorker(Looper looper) {
4458 public void handleMessage(Message msg) {
4460 case MESSAGE_RECONSIDER_RANKING:
4461 handleRankingReconsideration(msg);
4463 case MESSAGE_RANKING_SORT:
4464 handleRankingSort();
4469 public void requestSort() {
4470 removeMessages(MESSAGE_RANKING_SORT);
4471 Message msg = Message.obtain();
4472 msg.what = MESSAGE_RANKING_SORT;
4476 public void requestReconsideration(RankingReconsideration recon) {
4477 Message m = Message.obtain(this,
4478 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4479 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4480 sendMessageDelayed(m, delay);
4485 // ============================================================================
4486 static int clamp(int x, int low, int high) {
4487 return (x < low) ? low : ((x > high) ? high : x);
4490 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4491 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
4492 if (!manager.isEnabled()) {
4496 AccessibilityEvent event =
4497 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4498 event.setPackageName(packageName);
4499 event.setClassName(Notification.class.getName());
4500 event.setParcelableData(notification);
4501 CharSequence tickerText = notification.tickerText;
4502 if (!TextUtils.isEmpty(tickerText)) {
4503 event.getText().add(tickerText);
4506 manager.sendAccessibilityEvent(event);
4510 * Removes all NotificationsRecords with the same key as the given notification record
4511 * from both lists. Do not call this method while iterating over either list.
4513 @GuardedBy("mNotificationLock")
4514 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4515 // Remove from both lists, either list could have a separate Record for what is
4516 // effectively the same notification.
4517 boolean wasPosted = false;
4518 NotificationRecord recordInList = null;
4519 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4521 mNotificationList.remove(recordInList);
4522 mNotificationsByKey.remove(recordInList.sbn.getKey());
4525 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4527 mEnqueuedNotifications.remove(recordInList);
4532 @GuardedBy("mNotificationLock")
4533 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4534 boolean wasPosted, String listenerName) {
4535 final String canceledKey = r.getKey();
4538 recordCallerLocked(r);
4542 if (r.getNotification().deleteIntent != null) {
4544 r.getNotification().deleteIntent.send();
4545 } catch (PendingIntent.CanceledException ex) {
4546 // do nothing - there's no relevant way to recover, and
4547 // no reason to let this propagate
4548 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4553 // Only cancel these if this notification actually got to be posted.
4556 if (r.getNotification().getSmallIcon() != null) {
4557 if (reason != REASON_SNOOZED) {
4558 r.isCanceled = true;
4560 mListeners.notifyRemovedLocked(r.sbn, reason);
4561 mHandler.post(new Runnable() {
4564 mGroupHelper.onNotificationRemoved(r.sbn);
4570 if (canceledKey.equals(mSoundNotificationKey)) {
4571 mSoundNotificationKey = null;
4572 final long identity = Binder.clearCallingIdentity();
4574 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4575 if (player != null) {
4578 } catch (RemoteException e) {
4580 Binder.restoreCallingIdentity(identity);
4585 if (canceledKey.equals(mVibrateNotificationKey)) {
4586 mVibrateNotificationKey = null;
4587 long identity = Binder.clearCallingIdentity();
4592 Binder.restoreCallingIdentity(identity);
4597 mLights.remove(canceledKey);
4600 // Record usage stats
4601 // TODO: add unbundling stats?
4604 case REASON_CANCEL_ALL:
4605 case REASON_LISTENER_CANCEL:
4606 case REASON_LISTENER_CANCEL_ALL:
4607 mUsageStats.registerDismissedByUser(r);
4609 case REASON_APP_CANCEL:
4610 case REASON_APP_CANCEL_ALL:
4611 mUsageStats.registerRemovedByApp(r);
4615 String groupKey = r.getGroupKey();
4616 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4617 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4618 mSummaryByGroupKey.remove(groupKey);
4620 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4621 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4622 summaries.remove(r.sbn.getPackageName());
4625 // Save it for users of getHistoricalNotifications()
4626 mArchive.record(r.sbn);
4628 final long now = System.currentTimeMillis();
4629 MetricsLogger.action(r.getLogMaker(now)
4630 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4631 .setType(MetricsEvent.TYPE_DISMISS)
4632 .setSubtype(reason));
4633 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4634 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), listenerName);
4638 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4639 * and none of the {@code mustNotHaveFlags}.
4641 void cancelNotification(final int callingUid, final int callingPid,
4642 final String pkg, final String tag, final int id,
4643 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4644 final int userId, final int reason, final ManagedServiceInfo listener) {
4645 // In enqueueNotificationInternal notifications are added by scheduling the
4646 // work on the worker handler. Hence, we also schedule the cancel on this
4647 // handler to avoid a scenario where an add notification call followed by a
4648 // remove notification call ends up in not removing the notification.
4649 mHandler.post(new Runnable() {
4652 String listenerName = listener == null ? null : listener.component.toShortString();
4653 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4654 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4656 synchronized (mNotificationLock) {
4657 // Look for the notification, searching both the posted and enqueued lists.
4658 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4660 // The notification was found, check if it should be removed.
4662 // Ideally we'd do this in the caller of this method. However, that would
4663 // require the caller to also find the notification.
4664 if (reason == REASON_CLICK) {
4665 mUsageStats.registerClickedByUser(r);
4668 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4671 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4675 // Cancel the notification.
4676 boolean wasPosted = removeFromNotificationListsLocked(r);
4677 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4678 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4680 updateLightsLocked();
4682 // No notification was found, assume that it is snoozed and cancel it.
4683 if (reason != REASON_SNOOZED) {
4684 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4696 * Determine whether the userId applies to the notification in question, either because
4697 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4699 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4701 // looking for USER_ALL notifications? match everything
4702 userId == UserHandle.USER_ALL
4703 // a notification sent to USER_ALL matches any query
4704 || r.getUserId() == UserHandle.USER_ALL
4705 // an exact user match
4706 || r.getUserId() == userId;
4710 * Determine whether the userId applies to the notification in question, either because
4711 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4712 * because it matches one of the users profiles.
4714 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4715 return notificationMatchesUserId(r, userId)
4716 || mUserProfiles.isCurrentProfile(r.getUserId());
4720 * Cancels all notifications from a given package that have all of the
4721 * {@code mustHaveFlags}.
4723 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4724 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4725 ManagedServiceInfo listener) {
4726 mHandler.post(new Runnable() {
4729 String listenerName = listener == null ? null : listener.component.toShortString();
4730 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4731 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4734 // Why does this parameter exist? Do we actually want to execute the above if doit
4740 synchronized (mNotificationLock) {
4741 FlagChecker flagChecker = (int flags) -> {
4742 if ((flags & mustHaveFlags) != mustHaveFlags) {
4745 if ((flags & mustNotHaveFlags) != 0) {
4750 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4751 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4752 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4753 listenerName, true /* wasPosted */);
4754 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4755 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4756 flagChecker, false /*includeCurrentProfiles*/, userId,
4757 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4758 mSnoozeHelper.cancel(userId, pkg);
4764 private interface FlagChecker {
4765 // Returns false if these flags do not pass the defined flag test.
4766 public boolean apply(int flags);
4769 @GuardedBy("mNotificationLock")
4770 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4771 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4772 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4773 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4774 ArrayList<NotificationRecord> canceledNotifications = null;
4775 for (int i = notificationList.size() - 1; i >= 0; --i) {
4776 NotificationRecord r = notificationList.get(i);
4777 if (includeCurrentProfiles) {
4778 if (!notificationMatchesCurrentProfiles(r, userId)) {
4781 } else if (!notificationMatchesUserId(r, userId)) {
4784 // Don't remove notifications to all, if there's no package name specified
4785 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4788 if (!flagChecker.apply(r.getFlags())) {
4791 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4794 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4797 if (canceledNotifications == null) {
4798 canceledNotifications = new ArrayList<>();
4800 notificationList.remove(i);
4801 mNotificationsByKey.remove(r.getKey());
4802 canceledNotifications.add(r);
4803 cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
4805 if (canceledNotifications != null) {
4806 final int M = canceledNotifications.size();
4807 for (int i = 0; i < M; i++) {
4808 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4809 listenerName, false /* sendDelete */, flagChecker);
4811 updateLightsLocked();
4815 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4816 ManagedServiceInfo listener) {
4817 String listenerName = listener == null ? null : listener.component.toShortString();
4818 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4823 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4824 snoozeCriterionId, listenerName));
4826 // Needs to post so that it can cancel notifications not yet enqueued.
4827 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4830 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4831 String listenerName = listener == null ? null : listener.component.toShortString();
4833 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4835 mSnoozeHelper.repost(key);
4839 @GuardedBy("mNotificationLock")
4840 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4841 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4842 mHandler.post(new Runnable() {
4845 synchronized (mNotificationLock) {
4846 String listenerName =
4847 listener == null ? null : listener.component.toShortString();
4848 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4849 null, userId, 0, 0, reason, listenerName);
4851 FlagChecker flagChecker = (int flags) -> {
4852 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4859 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4860 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4861 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4862 listenerName, true);
4863 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4864 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4865 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4866 reason, listenerName, false);
4867 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4873 // Warning: The caller is responsible for invoking updateLightsLocked().
4874 @GuardedBy("mNotificationLock")
4875 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4876 String listenerName, boolean sendDelete, FlagChecker flagChecker) {
4877 Notification n = r.getNotification();
4878 if (!n.isGroupSummary()) {
4882 String pkg = r.sbn.getPackageName();
4885 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4889 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4890 sendDelete, true, flagChecker);
4891 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4892 listenerName, sendDelete, false, flagChecker);
4895 @GuardedBy("mNotificationLock")
4896 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4897 NotificationRecord parentNotification, int callingUid, int callingPid,
4898 String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
4899 final String pkg = parentNotification.sbn.getPackageName();
4900 final int userId = parentNotification.getUserId();
4901 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4902 for (int i = notificationList.size() - 1; i >= 0; i--) {
4903 final NotificationRecord childR = notificationList.get(i);
4904 final StatusBarNotification childSbn = childR.sbn;
4905 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4906 childR.getGroupKey().equals(parentNotification.getGroupKey())
4907 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0
4908 && (flagChecker == null || flagChecker.apply(childR.getFlags()))) {
4909 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4910 childSbn.getTag(), userId, 0, 0, reason, listenerName);
4911 notificationList.remove(i);
4912 mNotificationsByKey.remove(childR.getKey());
4913 cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName);
4918 @GuardedBy("mNotificationLock")
4919 void updateLightsLocked()
4921 // handle notification lights
4922 NotificationRecord ledNotification = null;
4923 while (ledNotification == null && !mLights.isEmpty()) {
4924 final String owner = mLights.get(mLights.size() - 1);
4925 ledNotification = mNotificationsByKey.get(owner);
4926 if (ledNotification == null) {
4927 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4928 mLights.remove(owner);
4932 // Don't flash while we are in a call or screen is on
4933 if (ledNotification == null || mInCall || mScreenOn) {
4934 mNotificationLight.turnOff();
4936 NotificationRecord.Light light = ledNotification.getLight();
4937 if (light != null && mNotificationPulseEnabled) {
4939 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4940 light.onMs, light.offMs);
4945 @GuardedBy("mNotificationLock")
4946 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4947 String groupKey, int userId) {
4948 List<NotificationRecord> records = new ArrayList<>();
4949 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4951 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4956 @GuardedBy("mNotificationLock")
4957 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4958 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4959 List<NotificationRecord> records = new ArrayList<>();
4960 final int len = list.size();
4961 for (int i = 0; i < len; i++) {
4962 NotificationRecord r = list.get(i);
4963 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4964 && r.sbn.getPackageName().equals(pkg)) {
4971 // Searches both enqueued and posted notifications by key.
4972 // TODO: need to combine a bunch of these getters with slightly different behavior.
4973 // TODO: Should enqueuing just add to mNotificationsByKey instead?
4974 @GuardedBy("mNotificationLock")
4975 private NotificationRecord findNotificationByKeyLocked(String key) {
4976 NotificationRecord r;
4977 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4980 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4986 @GuardedBy("mNotificationLock")
4987 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
4988 NotificationRecord r;
4989 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4992 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4999 @GuardedBy("mNotificationLock")
5000 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5001 String pkg, String tag, int id, int userId) {
5002 final int len = list.size();
5003 for (int i = 0; i < len; i++) {
5004 NotificationRecord r = list.get(i);
5005 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
5006 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
5013 @GuardedBy("mNotificationLock")
5014 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
5016 final int N = list.size();
5017 for (int i = 0; i < N; i++) {
5018 if (key.equals(list.get(i).getKey())) {
5025 @GuardedBy("mNotificationLock")
5026 int indexOfNotificationLocked(String key) {
5027 final int N = mNotificationList.size();
5028 for (int i = 0; i < N; i++) {
5029 if (key.equals(mNotificationList.get(i).getKey())) {
5036 private void updateNotificationPulse() {
5037 synchronized (mNotificationLock) {
5038 updateLightsLocked();
5042 protected boolean isCallingUidSystem() {
5043 final int uid = Binder.getCallingUid();
5044 return uid == Process.SYSTEM_UID;
5047 protected boolean isUidSystemOrPhone(int uid) {
5048 final int appid = UserHandle.getAppId(uid);
5049 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
5052 // TODO: Most calls should probably move to isCallerSystem.
5053 protected boolean isCallerSystemOrPhone() {
5054 return isUidSystemOrPhone(Binder.getCallingUid());
5057 private void checkCallerIsSystemOrShell() {
5058 if (Binder.getCallingUid() == Process.SHELL_UID) {
5061 checkCallerIsSystem();
5064 private void checkCallerIsSystem() {
5065 if (isCallerSystemOrPhone()) {
5068 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
5071 private void checkCallerIsSystemOrSameApp(String pkg) {
5072 if (isCallerSystemOrPhone()) {
5075 checkCallerIsSameApp(pkg);
5078 private boolean isCallerInstantApp(String pkg) {
5079 // System is always allowed to act for ephemeral apps.
5080 if (isCallerSystemOrPhone()) {
5084 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
5087 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
5088 UserHandle.getCallingUserId());
5090 throw new SecurityException("Unknown package " + pkg);
5092 return ai.isInstantApp();
5093 } catch (RemoteException re) {
5094 throw new SecurityException("Unknown package " + pkg, re);
5099 private void checkCallerIsSameApp(String pkg) {
5100 final int uid = Binder.getCallingUid();
5102 ApplicationInfo ai = mPackageManager.getApplicationInfo(
5103 pkg, 0, UserHandle.getCallingUserId());
5105 throw new SecurityException("Unknown package " + pkg);
5107 if (!UserHandle.isSameApp(ai.uid, uid)) {
5108 throw new SecurityException("Calling uid " + uid + " gave package "
5109 + pkg + " which is owned by uid " + ai.uid);
5111 } catch (RemoteException re) {
5112 throw new SecurityException("Unknown package " + pkg + "\n" + re);
5116 private static String callStateToString(int state) {
5118 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
5119 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
5120 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
5121 default: return "CALL_STATE_UNKNOWN_" + state;
5125 private void listenForCallState() {
5126 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
5128 public void onCallStateChanged(int state, String incomingNumber) {
5129 if (mCallState == state) return;
5130 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
5133 }, PhoneStateListener.LISTEN_CALL_STATE);
5137 * Generates a NotificationRankingUpdate from 'sbns', considering only
5138 * notifications visible to the given listener.
5140 @GuardedBy("mNotificationLock")
5141 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
5142 final int N = mNotificationList.size();
5143 ArrayList<String> keys = new ArrayList<String>(N);
5144 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
5145 ArrayList<Integer> importance = new ArrayList<>(N);
5146 Bundle overrideGroupKeys = new Bundle();
5147 Bundle visibilityOverrides = new Bundle();
5148 Bundle suppressedVisualEffects = new Bundle();
5149 Bundle explanation = new Bundle();
5150 Bundle channels = new Bundle();
5151 Bundle overridePeople = new Bundle();
5152 Bundle snoozeCriteria = new Bundle();
5153 Bundle showBadge = new Bundle();
5154 for (int i = 0; i < N; i++) {
5155 NotificationRecord record = mNotificationList.get(i);
5156 if (!isVisibleToListener(record.sbn, info)) {
5159 final String key = record.sbn.getKey();
5161 importance.add(record.getImportance());
5162 if (record.getImportanceExplanation() != null) {
5163 explanation.putCharSequence(key, record.getImportanceExplanation());
5165 if (record.isIntercepted()) {
5166 interceptedKeys.add(key);
5169 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
5170 if (record.getPackageVisibilityOverride()
5171 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
5172 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
5174 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
5175 channels.putParcelable(key, record.getChannel());
5176 overridePeople.putStringArrayList(key, record.getPeopleOverride());
5177 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
5178 showBadge.putBoolean(key, record.canShowBadge());
5180 final int M = keys.size();
5181 String[] keysAr = keys.toArray(new String[M]);
5182 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
5183 int[] importanceAr = new int[M];
5184 for (int i = 0; i < M; i++) {
5185 importanceAr[i] = importance.get(i);
5187 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
5188 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
5189 channels, overridePeople, snoozeCriteria, showBadge);
5192 boolean hasCompanionDevice(ManagedServiceInfo info) {
5193 if (mCompanionManager == null) {
5194 mCompanionManager = getCompanionManager();
5196 // Companion mgr doesn't exist on all device types
5197 if (mCompanionManager == null) {
5200 long identity = Binder.clearCallingIdentity();
5202 List<String> associations = mCompanionManager.getAssociations(
5203 info.component.getPackageName(), info.userid);
5204 if (!ArrayUtils.isEmpty(associations)) {
5207 } catch (SecurityException se) {
5208 // Not a privileged listener
5209 } catch (RemoteException re) {
5210 Slog.e(TAG, "Cannot reach companion device service", re);
5211 } catch (Exception e) {
5212 Slog.e(TAG, "Cannot verify listener " + info, e);
5214 Binder.restoreCallingIdentity(identity);
5219 protected ICompanionDeviceManager getCompanionManager() {
5220 return ICompanionDeviceManager.Stub.asInterface(
5221 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
5224 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5225 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5228 // TODO: remove this for older listeners.
5232 private boolean isPackageSuspendedForUser(String pkg, int uid) {
5233 int userId = UserHandle.getUserId(uid);
5235 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
5236 } catch (RemoteException re) {
5237 throw new SecurityException("Could not talk to package manager service");
5238 } catch (IllegalArgumentException ex) {
5239 // Package not found.
5244 private class TrimCache {
5245 StatusBarNotification heavy;
5246 StatusBarNotification sbnClone;
5247 StatusBarNotification sbnCloneLight;
5249 TrimCache(StatusBarNotification sbn) {
5253 StatusBarNotification ForListener(ManagedServiceInfo info) {
5254 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5255 if (sbnCloneLight == null) {
5256 sbnCloneLight = heavy.cloneLight();
5258 return sbnCloneLight;
5260 if (sbnClone == null) {
5261 sbnClone = heavy.clone();
5268 public class NotificationAssistants extends ManagedServices {
5269 static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
5271 public NotificationAssistants(IPackageManager pm) {
5272 super(getContext(), mNotificationLock, mUserProfiles, pm);
5276 protected Config getConfig() {
5277 Config c = new Config();
5278 c.caption = "notification assistant service";
5279 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5280 c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
5281 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5282 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5283 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5284 c.clientLabel = R.string.notification_ranker_binding_label;
5289 protected IInterface asInterface(IBinder binder) {
5290 return INotificationListener.Stub.asInterface(binder);
5294 protected boolean checkType(IInterface service) {
5295 return service instanceof INotificationListener;
5299 protected void onServiceAdded(ManagedServiceInfo info) {
5300 mListeners.registerGuestService(info);
5304 @GuardedBy("mNotificationLock")
5305 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5306 mListeners.unregisterService(removed.service, removed.userid);
5309 public void onNotificationEnqueued(final NotificationRecord r) {
5310 final StatusBarNotification sbn = r.sbn;
5311 TrimCache trimCache = new TrimCache(sbn);
5313 // There should be only one, but it's a list, so while we enforce
5314 // singularity elsewhere, we keep it general here, to avoid surprises.
5315 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5316 boolean sbnVisible = isVisibleToListener(sbn, info);
5321 final int importance = r.getImportance();
5322 final boolean fromUser = r.isImportanceFromUser();
5323 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5324 mHandler.post(new Runnable() {
5327 notifyEnqueued(info, sbnToPost);
5333 private void notifyEnqueued(final ManagedServiceInfo info,
5334 final StatusBarNotification sbn) {
5335 final INotificationListener assistant = (INotificationListener) info.service;
5336 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5338 assistant.onNotificationEnqueued(sbnHolder);
5339 } catch (RemoteException ex) {
5340 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5345 * asynchronously notify the assistant that a notification has been snoozed until a
5348 @GuardedBy("mNotificationLock")
5349 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5350 final String snoozeCriterionId) {
5351 TrimCache trimCache = new TrimCache(sbn);
5352 for (final ManagedServiceInfo info : getServices()) {
5353 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5354 mHandler.post(new Runnable() {
5357 final INotificationListener assistant =
5358 (INotificationListener) info.service;
5359 StatusBarNotificationHolder sbnHolder
5360 = new StatusBarNotificationHolder(sbnToPost);
5362 assistant.onNotificationSnoozedUntilContext(
5363 sbnHolder, snoozeCriterionId);
5364 } catch (RemoteException ex) {
5365 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5372 public boolean isEnabled() {
5373 return !getServices().isEmpty();
5377 public class NotificationListeners extends ManagedServices {
5378 static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
5380 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5382 public NotificationListeners(IPackageManager pm) {
5383 super(getContext(), mNotificationLock, mUserProfiles, pm);
5388 protected Config getConfig() {
5389 Config c = new Config();
5390 c.caption = "notification listener";
5391 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5392 c.xmlTag = TAG_ENABLED_NOTIFICATION_LISTENERS;
5393 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5394 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5395 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5396 c.clientLabel = R.string.notification_listener_binding_label;
5401 protected IInterface asInterface(IBinder binder) {
5402 return INotificationListener.Stub.asInterface(binder);
5406 protected boolean checkType(IInterface service) {
5407 return service instanceof INotificationListener;
5411 public void onServiceAdded(ManagedServiceInfo info) {
5412 final INotificationListener listener = (INotificationListener) info.service;
5413 final NotificationRankingUpdate update;
5414 synchronized (mNotificationLock) {
5415 update = makeRankingUpdateLocked(info);
5418 listener.onListenerConnected(update);
5419 } catch (RemoteException e) {
5425 @GuardedBy("mNotificationLock")
5426 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5427 if (removeDisabledHints(removed)) {
5428 updateListenerHintsLocked();
5429 updateEffectsSuppressorLocked();
5431 mLightTrimListeners.remove(removed);
5434 @GuardedBy("mNotificationLock")
5435 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5436 if (trim == TRIM_LIGHT) {
5437 mLightTrimListeners.add(info);
5439 mLightTrimListeners.remove(info);
5443 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5444 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5448 * asynchronously notify all listeners about a new notification
5451 * Also takes care of removing a notification that has been visible to a listener before,
5452 * but isn't anymore.
5454 @GuardedBy("mNotificationLock")
5455 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5456 // Lazily initialized snapshots of the notification.
5457 TrimCache trimCache = new TrimCache(sbn);
5459 for (final ManagedServiceInfo info : getServices()) {
5460 boolean sbnVisible = isVisibleToListener(sbn, info);
5461 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5462 // This notification hasn't been and still isn't visible -> ignore.
5463 if (!oldSbnVisible && !sbnVisible) {
5466 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5468 // This notification became invisible -> remove the old one.
5469 if (oldSbnVisible && !sbnVisible) {
5470 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5471 mHandler.post(new Runnable() {
5474 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5480 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5481 mHandler.post(new Runnable() {
5484 notifyPosted(info, sbnToPost, update);
5491 * asynchronously notify all listeners about a removed notification
5493 @GuardedBy("mNotificationLock")
5494 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5495 // make a copy in case changes are made to the underlying Notification object
5496 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5498 final StatusBarNotification sbnLight = sbn.cloneLight();
5499 for (final ManagedServiceInfo info : getServices()) {
5500 if (!isVisibleToListener(sbn, info)) {
5503 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5504 mHandler.post(new Runnable() {
5507 notifyRemoved(info, sbnLight, update, reason);
5514 * asynchronously notify all listeners about a reordering of notifications
5516 @GuardedBy("mNotificationLock")
5517 public void notifyRankingUpdateLocked() {
5518 for (final ManagedServiceInfo serviceInfo : getServices()) {
5519 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5522 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5523 mHandler.post(new Runnable() {
5526 notifyRankingUpdate(serviceInfo, update);
5532 @GuardedBy("mNotificationLock")
5533 public void notifyListenerHintsChangedLocked(final int hints) {
5534 for (final ManagedServiceInfo serviceInfo : getServices()) {
5535 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5538 mHandler.post(new Runnable() {
5541 notifyListenerHintsChanged(serviceInfo, hints);
5547 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5548 for (final ManagedServiceInfo serviceInfo : getServices()) {
5549 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5552 mHandler.post(new Runnable() {
5555 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5561 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5562 final NotificationChannel channel, final int modificationType) {
5563 if (channel == null) {
5566 for (final ManagedServiceInfo serviceInfo : getServices()) {
5567 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5571 mHandler.post(new Runnable() {
5574 if (hasCompanionDevice(serviceInfo)) {
5575 notifyNotificationChannelChanged(
5576 serviceInfo, pkg, user, channel, modificationType);
5583 protected void notifyNotificationChannelGroupChanged(
5584 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5585 final int modificationType) {
5586 if (group == null) {
5589 for (final ManagedServiceInfo serviceInfo : getServices()) {
5590 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5594 mHandler.post(new Runnable() {
5597 if (hasCompanionDevice(serviceInfo)) {
5598 notifyNotificationChannelGroupChanged(
5599 serviceInfo, pkg, user, group, modificationType);
5606 private void notifyPosted(final ManagedServiceInfo info,
5607 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5608 final INotificationListener listener = (INotificationListener) info.service;
5609 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5611 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5612 } catch (RemoteException ex) {
5613 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5617 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5618 NotificationRankingUpdate rankingUpdate, int reason) {
5619 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5622 final INotificationListener listener = (INotificationListener) info.service;
5623 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5625 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5626 } catch (RemoteException ex) {
5627 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5631 private void notifyRankingUpdate(ManagedServiceInfo info,
5632 NotificationRankingUpdate rankingUpdate) {
5633 final INotificationListener listener = (INotificationListener) info.service;
5635 listener.onNotificationRankingUpdate(rankingUpdate);
5636 } catch (RemoteException ex) {
5637 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5641 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5642 final INotificationListener listener = (INotificationListener) info.service;
5644 listener.onListenerHintsChanged(hints);
5645 } catch (RemoteException ex) {
5646 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5650 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5651 int interruptionFilter) {
5652 final INotificationListener listener = (INotificationListener) info.service;
5654 listener.onInterruptionFilterChanged(interruptionFilter);
5655 } catch (RemoteException ex) {
5656 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5660 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5661 final String pkg, final UserHandle user, final NotificationChannel channel,
5662 final int modificationType) {
5663 final INotificationListener listener = (INotificationListener) info.service;
5665 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5666 } catch (RemoteException ex) {
5667 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5671 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5672 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5673 final int modificationType) {
5674 final INotificationListener listener = (INotificationListener) info.service;
5676 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5677 } catch (RemoteException ex) {
5678 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5682 public boolean isListenerPackage(String packageName) {
5683 if (packageName == null) {
5686 // TODO: clean up locking object later
5687 synchronized (mNotificationLock) {
5688 for (final ManagedServiceInfo serviceInfo : getServices()) {
5689 if (packageName.equals(serviceInfo.component.getPackageName())) {
5698 public static final class DumpFilter {
5699 public boolean filtered = false;
5700 public String pkgFilter;
5703 public boolean stats;
5704 public boolean redact = true;
5705 public boolean proto = false;
5707 public static DumpFilter parseFromArguments(String[] args) {
5708 final DumpFilter filter = new DumpFilter();
5709 for (int ai = 0; ai < args.length; ai++) {
5710 final String a = args[ai];
5711 if ("--proto".equals(args[0])) {
5712 filter.proto = true;
5714 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5715 filter.redact = false;
5716 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5717 if (ai < args.length-1) {
5719 filter.pkgFilter = args[ai].trim().toLowerCase();
5720 if (filter.pkgFilter.isEmpty()) {
5721 filter.pkgFilter = null;
5723 filter.filtered = true;
5726 } else if ("--zen".equals(a) || "zen".equals(a)) {
5727 filter.filtered = true;
5729 } else if ("--stats".equals(a)) {
5730 filter.stats = true;
5731 if (ai < args.length-1) {
5733 filter.since = Long.parseLong(args[ai]);
5742 public boolean matches(StatusBarNotification sbn) {
5743 if (!filtered) return true;
5744 return zen ? true : sbn != null
5745 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5748 public boolean matches(ComponentName component) {
5749 if (!filtered) return true;
5750 return zen ? true : component != null && matches(component.getPackageName());
5753 public boolean matches(String pkg) {
5754 if (!filtered) return true;
5755 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5759 public String toString() {
5760 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5765 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5766 * binder without sending large amounts of data over a oneway transaction.
5768 private static final class StatusBarNotificationHolder
5769 extends IStatusBarNotificationHolder.Stub {
5770 private StatusBarNotification mValue;
5772 public StatusBarNotificationHolder(StatusBarNotification value) {
5776 /** Get the held value and clear it. This function should only be called once per holder */
5778 public StatusBarNotification get() {
5779 StatusBarNotification value = mValue;
5785 private class ShellCmd extends ShellCommand {
5786 public static final String USAGE = "help\n"
5787 + "allow_listener COMPONENT\n"
5788 + "disallow_listener COMPONENT\n"
5789 + "set_assistant COMPONENT\n"
5790 + "remove_assistant COMPONENT\n"
5791 + "allow_dnd PACKAGE\n"
5792 + "disallow_dnd PACKAGE";
5795 public int onCommand(String cmd) {
5797 return handleDefaultCommands(cmd);
5799 final PrintWriter pw = getOutPrintWriter();
5803 getBinderService().setNotificationPolicyAccessGranted(
5804 getNextArgRequired(), true);
5808 case "disallow_dnd": {
5809 getBinderService().setNotificationPolicyAccessGranted(
5810 getNextArgRequired(), false);
5813 case "allow_listener": {
5814 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5816 pw.println("Invalid listener - must be a ComponentName");
5819 getBinderService().setNotificationListenerAccessGranted(cn, true);
5822 case "disallow_listener": {
5823 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5825 pw.println("Invalid listener - must be a ComponentName");
5828 getBinderService().setNotificationListenerAccessGranted(cn, false);
5831 case "allow_assistant": {
5832 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5834 pw.println("Invalid assistant - must be a ComponentName");
5837 getBinderService().setNotificationAssistantAccessGranted(cn, true);
5840 case "disallow_assistant": {
5841 ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
5843 pw.println("Invalid assistant - must be a ComponentName");
5846 getBinderService().setNotificationAssistantAccessGranted(cn, false);
5851 return handleDefaultCommands(cmd);
5853 } catch (Exception e) {
5854 pw.println("Error occurred. Check logcat for details. " + e.getMessage());
5855 Slog.e(TAG, "Error running shell command", e);
5861 public void onHelp() {
5862 getOutPrintWriter().println(USAGE);