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.service.notification.NotificationListenerService
25 .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
26 import static android.service.notification.NotificationListenerService
27 .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
28 import static android.service.notification.NotificationListenerService
29 .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
30 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
31 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
32 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
33 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
34 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
35 import static android.service.notification.NotificationListenerService.REASON_CLICK;
36 import static android.service.notification.NotificationListenerService.REASON_ERROR;
37 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
38 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
39 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
40 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
41 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
42 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
43 import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
44 import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
45 import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
46 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
47 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
48 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
49 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
50 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
51 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
52 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
53 import static android.service.notification.NotificationListenerService.TRIM_FULL;
54 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
56 import static android.view.Display.DEFAULT_DISPLAY;
57 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
58 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
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.PackageInfo;
92 import android.content.pm.PackageManager;
93 import android.content.pm.PackageManager.NameNotFoundException;
94 import android.content.pm.ParceledListSlice;
95 import android.content.res.Resources;
96 import android.database.ContentObserver;
97 import android.media.AudioManager;
98 import android.media.AudioManagerInternal;
99 import android.media.IRingtonePlayer;
100 import android.media.ToneGenerator;
101 import android.net.Uri;
102 import android.os.Binder;
103 import android.os.Build;
104 import android.os.Bundle;
105 import android.os.Environment;
106 import android.os.Handler;
107 import android.os.HandlerThread;
108 import android.os.IBinder;
109 import android.os.IInterface;
110 import android.os.Looper;
111 import android.os.Message;
112 import android.os.Process;
113 import android.os.RemoteException;
114 import android.os.ServiceManager;
115 import android.os.SystemClock;
116 import android.os.SystemProperties;
117 import android.os.UserHandle;
118 import android.os.Vibrator;
119 import android.os.VibrationEffect;
120 import android.provider.Settings;
121 import android.service.notification.Adjustment;
122 import android.service.notification.Condition;
123 import android.service.notification.IConditionProvider;
124 import android.service.notification.INotificationListener;
125 import android.service.notification.IStatusBarNotificationHolder;
126 import android.service.notification.NotificationAssistantService;
127 import android.service.notification.NotificationListenerService;
128 import android.service.notification.NotificationRankingUpdate;
129 import android.service.notification.NotificationRecordProto;
130 import android.service.notification.NotificationServiceDumpProto;
131 import android.service.notification.NotificationServiceProto;
132 import android.service.notification.SnoozeCriterion;
133 import android.service.notification.StatusBarNotification;
134 import android.service.notification.ZenModeConfig;
135 import android.service.notification.ZenModeProto;
136 import android.telecom.TelecomManager;
137 import android.telephony.PhoneStateListener;
138 import android.telephony.TelephonyManager;
139 import android.text.TextUtils;
140 import android.util.ArrayMap;
141 import android.util.ArraySet;
142 import android.util.AtomicFile;
143 import android.util.Log;
144 import android.util.Slog;
145 import android.util.SparseArray;
146 import android.util.Xml;
147 import android.util.proto.ProtoOutputStream;
148 import android.view.WindowManagerInternal;
149 import android.view.accessibility.AccessibilityEvent;
150 import android.view.accessibility.AccessibilityManager;
151 import android.widget.Toast;
153 import com.android.internal.R;
154 import com.android.internal.annotations.GuardedBy;
155 import com.android.internal.annotations.VisibleForTesting;
156 import com.android.internal.logging.MetricsLogger;
157 import com.android.internal.logging.nano.MetricsProto;
158 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
159 import com.android.internal.statusbar.NotificationVisibility;
160 import com.android.internal.util.ArrayUtils;
161 import com.android.internal.util.DumpUtils;
162 import com.android.internal.util.FastXmlSerializer;
163 import com.android.internal.util.Preconditions;
164 import com.android.server.DeviceIdleController;
165 import com.android.server.EventLogTags;
166 import com.android.server.LocalServices;
167 import com.android.server.SystemService;
168 import com.android.server.lights.Light;
169 import com.android.server.lights.LightsManager;
170 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
171 import com.android.server.policy.PhoneWindowManager;
172 import com.android.server.statusbar.StatusBarManagerInternal;
173 import com.android.server.notification.ManagedServices.UserProfiles;
175 import libcore.io.IoUtils;
177 import org.json.JSONException;
178 import org.json.JSONObject;
179 import org.xmlpull.v1.XmlPullParser;
180 import org.xmlpull.v1.XmlPullParserException;
181 import org.xmlpull.v1.XmlSerializer;
183 import java.io.ByteArrayInputStream;
184 import java.io.ByteArrayOutputStream;
186 import java.io.FileDescriptor;
187 import java.io.FileInputStream;
188 import java.io.FileNotFoundException;
189 import java.io.FileOutputStream;
190 import java.io.IOException;
191 import java.io.InputStream;
192 import java.io.OutputStream;
193 import java.io.PrintWriter;
194 import java.nio.charset.StandardCharsets;
195 import java.util.ArrayDeque;
196 import java.util.ArrayList;
197 import java.util.Arrays;
198 import java.util.Iterator;
199 import java.util.List;
200 import java.util.Map;
201 import java.util.Map.Entry;
202 import java.util.Objects;
203 import java.util.concurrent.TimeUnit;
206 public class NotificationManagerService extends SystemService {
207 static final String TAG = "NotificationService";
208 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
209 public static final boolean ENABLE_CHILD_NOTIFICATIONS
210 = SystemProperties.getBoolean("debug.child_notifs", true);
212 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
213 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
216 static final int MESSAGE_TIMEOUT = 2;
217 static final int MESSAGE_SAVE_POLICY_FILE = 3;
218 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
219 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
220 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
222 // ranking thread messages
223 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
224 private static final int MESSAGE_RANKING_SORT = 1001;
226 static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
227 static final int SHORT_DELAY = 2000; // 2 seconds
229 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
231 static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
233 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
235 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
237 static final boolean ENABLE_BLOCKED_TOASTS = true;
239 // When #matchesCallFilter is called from the ringer, wait at most
240 // 3s to resolve the contacts. This timeout is required since
241 // ContactsProvider might take a long time to start up.
243 // Return STARRED_CONTACT when the timeout is hit in order to avoid
244 // missed calls in ZEN mode "Important".
245 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
246 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
247 ValidateNotificationPeople.STARRED_CONTACT;
249 /** notification_enqueue status value for a newly enqueued notification. */
250 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
252 /** notification_enqueue status value for an existing notification. */
253 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
255 /** notification_enqueue status value for an ignored notification. */
256 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
257 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
259 private static final long DELAY_FOR_ASSISTANT_TIME = 100;
261 private static final String ACTION_NOTIFICATION_TIMEOUT =
262 NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
263 private static final int REQUEST_CODE_TIMEOUT = 1;
264 private static final String SCHEME_TIMEOUT = "timeout";
265 private static final String EXTRA_KEY = "key";
267 private IActivityManager mAm;
268 private IPackageManager mPackageManager;
269 private PackageManager mPackageManagerClient;
270 AudioManager mAudioManager;
271 AudioManagerInternal mAudioManagerInternal;
272 @Nullable StatusBarManagerInternal mStatusBar;
274 private WindowManagerInternal mWindowManagerInternal;
275 private AlarmManager mAlarmManager;
276 private ICompanionDeviceManager mCompanionManager;
278 final IBinder mForegroundToken = new Binder();
279 private Handler mHandler;
280 private final HandlerThread mRankingThread = new HandlerThread("ranker",
281 Process.THREAD_PRIORITY_BACKGROUND);
283 private Light mNotificationLight;
284 Light mAttentionLight;
286 private long[] mFallbackVibrationPattern;
287 private boolean mUseAttentionLight;
288 boolean mSystemReady;
290 private boolean mDisableNotificationEffects;
291 private int mCallState;
292 private String mSoundNotificationKey;
293 private String mVibrateNotificationKey;
295 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
296 new SparseArray<ArraySet<ManagedServiceInfo>>();
297 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
298 private int mListenerHints; // right now, all hints are global
299 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
301 // for enabling and disabling notification pulse behavior
302 private boolean mScreenOn = true;
303 private boolean mInCall = false;
304 private boolean mNotificationPulseEnabled;
306 // for generating notification tones in-call
307 private ToneGenerator mInCallToneGenerator;
308 private final Object mInCallToneGeneratorLock = new Object();
310 // used as a mutex for access to all active notifications & listeners
311 final Object mNotificationLock = new Object();
312 @GuardedBy("mNotificationLock")
313 final ArrayList<NotificationRecord> mNotificationList =
314 new ArrayList<NotificationRecord>();
315 @GuardedBy("mNotificationLock")
316 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
317 new ArrayMap<String, NotificationRecord>();
318 @GuardedBy("mNotificationLock")
319 final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
320 @GuardedBy("mNotificationLock")
321 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
322 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
323 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
324 final PolicyAccess mPolicyAccess = new PolicyAccess();
326 // The last key in this list owns the hardware.
327 ArrayList<String> mLights = new ArrayList<>();
329 private AppOpsManager mAppOps;
330 private UsageStatsManagerInternal mAppUsageStats;
332 private Archive mArchive;
334 // Persistent storage for notification policy
335 private AtomicFile mPolicyFile;
337 private static final int DB_VERSION = 1;
339 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
340 private static final String ATTR_VERSION = "version";
342 private RankingHelper mRankingHelper;
344 private final UserProfiles mUserProfiles = new UserProfiles();
345 private NotificationListeners mListeners;
346 private NotificationAssistants mNotificationAssistants;
347 private ConditionProviders mConditionProviders;
348 private NotificationUsageStats mUsageStats;
350 private static final int MY_UID = Process.myUid();
351 private static final int MY_PID = Process.myPid();
352 private static final IBinder WHITELIST_TOKEN = new Binder();
353 private RankingHandler mRankingHandler;
354 private long mLastOverRateLogTime;
355 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
357 private SnoozeHelper mSnoozeHelper;
358 private GroupHelper mGroupHelper;
359 private boolean mIsTelevision;
361 private static class Archive {
362 final int mBufferSize;
363 final ArrayDeque<StatusBarNotification> mBuffer;
365 public Archive(int size) {
367 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
370 public String toString() {
371 final StringBuilder sb = new StringBuilder();
372 final int N = mBuffer.size();
373 sb.append("Archive (");
375 sb.append(" notification");
376 sb.append((N==1)?")":"s)");
377 return sb.toString();
380 public void record(StatusBarNotification nr) {
381 if (mBuffer.size() == mBufferSize) {
382 mBuffer.removeFirst();
385 // We don't want to store the heavy bits of the notification in the archive,
386 // but other clients in the system process might be using the object, so we
387 // store a (lightened) copy.
388 mBuffer.addLast(nr.cloneLight());
391 public Iterator<StatusBarNotification> descendingIterator() {
392 return mBuffer.descendingIterator();
395 public StatusBarNotification[] getArray(int count) {
396 if (count == 0) count = mBufferSize;
397 final StatusBarNotification[] a
398 = new StatusBarNotification[Math.min(count, mBuffer.size())];
399 Iterator<StatusBarNotification> iter = descendingIterator();
401 while (iter.hasNext() && i < count) {
402 a[i++] = iter.next();
409 private void readPolicyXml(InputStream stream, boolean forRestore)
410 throws XmlPullParserException, NumberFormatException, IOException {
411 final XmlPullParser parser = Xml.newPullParser();
412 parser.setInput(stream, StandardCharsets.UTF_8.name());
414 while (parser.next() != END_DOCUMENT) {
415 mZenModeHelper.readXml(parser, forRestore);
416 mRankingHelper.readXml(parser, forRestore);
420 private void loadPolicyFile() {
421 if (DBG) Slog.d(TAG, "loadPolicyFile");
422 synchronized (mPolicyFile) {
424 FileInputStream infile = null;
426 infile = mPolicyFile.openRead();
427 readPolicyXml(infile, false /*forRestore*/);
428 } catch (FileNotFoundException e) {
430 } catch (IOException e) {
431 Log.wtf(TAG, "Unable to read notification policy", e);
432 } catch (NumberFormatException e) {
433 Log.wtf(TAG, "Unable to parse notification policy", e);
434 } catch (XmlPullParserException e) {
435 Log.wtf(TAG, "Unable to parse notification policy", e);
437 IoUtils.closeQuietly(infile);
442 public void savePolicyFile() {
443 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
444 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
447 private void handleSavePolicyFile() {
448 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
449 synchronized (mPolicyFile) {
450 final FileOutputStream stream;
452 stream = mPolicyFile.startWrite();
453 } catch (IOException e) {
454 Slog.w(TAG, "Failed to save policy file", e);
459 writePolicyXml(stream, false /*forBackup*/);
460 mPolicyFile.finishWrite(stream);
461 } catch (IOException e) {
462 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
463 mPolicyFile.failWrite(stream);
466 BackupManager.dataChanged(getContext().getPackageName());
469 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
470 final XmlSerializer out = new FastXmlSerializer();
471 out.setOutput(stream, StandardCharsets.UTF_8.name());
472 out.startDocument(null, true);
473 out.startTag(null, TAG_NOTIFICATION_POLICY);
474 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
475 mZenModeHelper.writeXml(out, forBackup);
476 mRankingHelper.writeXml(out, forBackup);
477 out.endTag(null, TAG_NOTIFICATION_POLICY);
481 /** Use this to check if a package can post a notification or toast. */
482 private boolean checkNotificationOp(String pkg, int uid) {
483 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
484 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
487 private static final class ToastRecord
491 final ITransientNotification callback;
495 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
499 this.callback = callback;
500 this.duration = duration;
504 void update(int duration) {
505 this.duration = duration;
508 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
509 if (filter != null && !filter.matches(pkg)) return;
510 pw.println(prefix + this);
514 public final String toString()
516 return "ToastRecord{"
517 + Integer.toHexString(System.identityHashCode(this))
519 + " callback=" + callback
520 + " duration=" + duration;
525 final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
528 public void onSetDisabled(int status) {
529 synchronized (mNotificationLock) {
530 mDisableNotificationEffects =
531 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
532 if (disableNotificationEffects(null) != null) {
533 // cancel whatever's going on
534 long identity = Binder.clearCallingIdentity();
536 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
537 if (player != null) {
540 } catch (RemoteException e) {
542 Binder.restoreCallingIdentity(identity);
545 identity = Binder.clearCallingIdentity();
549 Binder.restoreCallingIdentity(identity);
556 public void onClearAll(int callingUid, int callingPid, int userId) {
557 synchronized (mNotificationLock) {
558 cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
559 /*includeCurrentProfiles*/ true);
564 public void onNotificationClick(int callingUid, int callingPid, String key) {
565 synchronized (mNotificationLock) {
566 NotificationRecord r = mNotificationsByKey.get(key);
568 Log.w(TAG, "No notification with key: " + key);
571 final long now = System.currentTimeMillis();
572 MetricsLogger.action(r.getLogMaker(now)
573 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
574 .setType(MetricsEvent.TYPE_ACTION));
575 EventLogTags.writeNotificationClicked(key,
576 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
578 StatusBarNotification sbn = r.sbn;
579 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
580 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
581 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
587 public void onNotificationActionClick(int callingUid, int callingPid, String key,
589 synchronized (mNotificationLock) {
590 NotificationRecord r = mNotificationsByKey.get(key);
592 Log.w(TAG, "No notification with key: " + key);
595 final long now = System.currentTimeMillis();
596 MetricsLogger.action(r.getLogMaker(now)
597 .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
598 .setType(MetricsEvent.TYPE_ACTION)
599 .setSubtype(actionIndex));
600 EventLogTags.writeNotificationActionClicked(key, actionIndex,
601 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
602 // TODO: Log action click via UsageStats.
607 public void onNotificationClear(int callingUid, int callingPid,
608 String pkg, String tag, int id, int userId) {
609 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
610 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
611 true, userId, REASON_CANCEL, null);
615 public void onPanelRevealed(boolean clearEffects, int items) {
616 MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
617 MetricsLogger.histogram(getContext(), "note_load", items);
618 EventLogTags.writeNotificationPanelRevealed(items);
625 public void onPanelHidden() {
626 MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
627 EventLogTags.writeNotificationPanelHidden();
631 public void clearEffects() {
632 synchronized (mNotificationLock) {
633 if (DBG) Slog.d(TAG, "clearEffects");
635 clearVibrateLocked();
641 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
642 int uid, int initialPid, String message, int userId) {
643 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
644 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
645 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
647 long ident = Binder.clearCallingIdentity();
649 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
650 "Bad notification posted from package " + pkg
652 } catch (RemoteException e) {
654 Binder.restoreCallingIdentity(ident);
658 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
659 NotificationVisibility[] noLongerVisibleKeys) {
660 synchronized (mNotificationLock) {
661 for (NotificationVisibility nv : newlyVisibleKeys) {
662 NotificationRecord r = mNotificationsByKey.get(nv.key);
663 if (r == null) continue;
664 r.setVisibility(true, nv.rank);
667 // Note that we might receive this event after notifications
668 // have already left the system, e.g. after dismissing from the
669 // shade. Hence not finding notifications in
670 // mNotificationsByKey is not an exceptional condition.
671 for (NotificationVisibility nv : noLongerVisibleKeys) {
672 NotificationRecord r = mNotificationsByKey.get(nv.key);
673 if (r == null) continue;
674 r.setVisibility(false, nv.rank);
681 public void onNotificationExpansionChanged(String key,
682 boolean userAction, boolean expanded) {
683 synchronized (mNotificationLock) {
684 NotificationRecord r = mNotificationsByKey.get(key);
686 r.stats.onExpansionChanged(userAction, expanded);
687 final long now = System.currentTimeMillis();
688 MetricsLogger.action(r.getLogMaker(now)
689 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
690 .setType(MetricsEvent.TYPE_DETAIL));
691 EventLogTags.writeNotificationExpansion(key,
692 userAction ? 1 : 0, expanded ? 1 : 0,
693 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
699 @GuardedBy("mNotificationLock")
700 private void clearSoundLocked() {
701 mSoundNotificationKey = null;
702 long identity = Binder.clearCallingIdentity();
704 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
705 if (player != null) {
708 } catch (RemoteException e) {
710 Binder.restoreCallingIdentity(identity);
714 @GuardedBy("mNotificationLock")
715 private void clearVibrateLocked() {
716 mVibrateNotificationKey = null;
717 long identity = Binder.clearCallingIdentity();
721 Binder.restoreCallingIdentity(identity);
725 @GuardedBy("mNotificationLock")
726 private void clearLightsLocked() {
729 updateLightsLocked();
732 private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
734 public void onReceive(Context context, Intent intent) {
735 String action = intent.getAction();
736 if (action == null) {
739 if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
740 final NotificationRecord record;
741 synchronized (mNotificationLock) {
742 record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
744 if (record != null) {
745 cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
746 record.sbn.getPackageName(), record.sbn.getTag(),
747 record.sbn.getId(), 0,
748 Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
749 REASON_TIMEOUT, null);
755 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
757 public void onReceive(Context context, Intent intent) {
758 String action = intent.getAction();
759 if (action == null) {
763 boolean queryRestart = false;
764 boolean queryRemove = false;
765 boolean packageChanged = false;
766 boolean cancelNotifications = true;
767 int reason = REASON_PACKAGE_CHANGED;
769 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
770 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
771 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
772 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
773 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
774 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
775 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
776 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
777 UserHandle.USER_ALL);
778 String pkgList[] = null;
779 int uidList[] = null;
780 boolean removingPackage = queryRemove &&
781 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
782 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
783 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
784 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
785 uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
786 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
787 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
788 reason = REASON_PACKAGE_SUSPENDED;
789 } else if (queryRestart) {
790 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
791 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
793 Uri uri = intent.getData();
797 String pkgName = uri.getSchemeSpecificPart();
798 if (pkgName == null) {
801 if (packageChanged) {
802 // We cancel notifications for packages which have just been disabled
804 final int enabled = mPackageManager.getApplicationEnabledSetting(
806 changeUserId != UserHandle.USER_ALL ? changeUserId :
807 UserHandle.USER_SYSTEM);
808 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
809 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
810 cancelNotifications = false;
812 } catch (IllegalArgumentException e) {
813 // Package doesn't exist; probably racing with uninstall.
814 // cancelNotifications is already true, so nothing to do here.
816 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
818 } catch (RemoteException e) {
819 // Failed to talk to PackageManagerService Should never happen!
822 pkgList = new String[]{pkgName};
823 uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
825 if (pkgList != null && (pkgList.length > 0)) {
826 for (String pkgName : pkgList) {
827 if (cancelNotifications) {
828 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
829 !queryRestart, changeUserId, reason, null);
833 mListeners.onPackagesChanged(removingPackage, pkgList);
834 mNotificationAssistants.onPackagesChanged(removingPackage, pkgList);
835 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
836 mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
842 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
844 public void onReceive(Context context, Intent intent) {
845 String action = intent.getAction();
847 if (action.equals(Intent.ACTION_SCREEN_ON)) {
848 // Keep track of screen on/off state, but do not turn off the notification light
849 // until user passes through the lock screen or views the notification.
851 updateNotificationPulse();
852 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
854 updateNotificationPulse();
855 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
856 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
857 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
858 updateNotificationPulse();
859 synchronized (mInCallToneGeneratorLock) {
861 if (mInCallToneGenerator == null) {
862 int relativeToneVolume = getContext().getResources().getInteger(
863 R.integer.config_inCallNotificationVolumeRelative);
864 if (relativeToneVolume < ToneGenerator.MIN_VOLUME
865 || relativeToneVolume > ToneGenerator.MAX_VOLUME) {
866 relativeToneVolume = ToneGenerator.MAX_VOLUME;
869 mInCallToneGenerator = new ToneGenerator(
870 AudioManager.STREAM_VOICE_CALL, relativeToneVolume);
871 } catch (RuntimeException e) {
872 Log.e(TAG, "Error creating local tone generator: " + e);
873 mInCallToneGenerator = null;
877 if (mInCallToneGenerator != null) {
878 mInCallToneGenerator.release();
879 mInCallToneGenerator = null;
883 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
884 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
885 if (userHandle >= 0) {
886 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
887 REASON_USER_STOPPED, null);
889 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
890 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
891 if (userHandle >= 0) {
892 cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
893 REASON_PROFILE_TURNED_OFF, null);
895 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
896 // turn off LED when user passes through lock screen
897 mNotificationLight.turnOff();
898 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
899 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
900 // reload per-user settings
901 mSettingsObserver.update(null);
902 mUserProfiles.updateCache(context);
903 // Refresh managed services
904 mConditionProviders.onUserSwitched(user);
905 mListeners.onUserSwitched(user);
906 mNotificationAssistants.onUserSwitched(user);
907 mZenModeHelper.onUserSwitched(user);
908 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
909 mUserProfiles.updateCache(context);
910 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
911 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
912 mZenModeHelper.onUserRemoved(user);
913 mRankingHelper.onUserRemoved(user);
915 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
916 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
917 mConditionProviders.onUserUnlocked(user);
918 mListeners.onUserUnlocked(user);
919 mNotificationAssistants.onUserUnlocked(user);
920 mZenModeHelper.onUserUnlocked(user);
925 private final class SettingsObserver extends ContentObserver {
926 private final Uri NOTIFICATION_BADGING_URI
927 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
928 private final Uri NOTIFICATION_LIGHT_PULSE_URI
929 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
930 private final Uri NOTIFICATION_RATE_LIMIT_URI
931 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
933 SettingsObserver(Handler handler) {
938 ContentResolver resolver = getContext().getContentResolver();
939 resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
940 false, this, UserHandle.USER_ALL);
941 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
942 false, this, UserHandle.USER_ALL);
943 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
944 false, this, UserHandle.USER_ALL);
948 @Override public void onChange(boolean selfChange, Uri uri) {
952 public void update(Uri uri) {
953 ContentResolver resolver = getContext().getContentResolver();
954 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
955 boolean pulseEnabled = Settings.System.getIntForUser(resolver,
956 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
957 if (mNotificationPulseEnabled != pulseEnabled) {
958 mNotificationPulseEnabled = pulseEnabled;
959 updateNotificationPulse();
962 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
963 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
964 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
966 if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
967 mRankingHelper.updateBadgingEnabled();
972 private SettingsObserver mSettingsObserver;
973 private ZenModeHelper mZenModeHelper;
975 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
976 int[] ar = r.getIntArray(resid);
980 final int len = ar.length > maxlen ? maxlen : ar.length;
981 long[] out = new long[len];
982 for (int i=0; i<len; i++) {
988 public NotificationManagerService(Context context) {
990 Notification.processWhitelistToken = WHITELIST_TOKEN;
993 // TODO - replace these methods with a single VisibleForTesting constructor
995 void setAudioManager(AudioManager audioMananger) {
996 mAudioManager = audioMananger;
1000 void setVibrator(Vibrator vibrator) {
1001 mVibrator = vibrator;
1005 void setLights(Light light) {
1006 mNotificationLight = light;
1007 mAttentionLight = light;
1008 mNotificationPulseEnabled = true;
1012 void setScreenOn(boolean on) {
1017 int getNotificationRecordCount() {
1018 synchronized (mNotificationLock) {
1019 int count = mNotificationList.size() + mNotificationsByKey.size()
1020 + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
1021 // subtract duplicates
1022 for (NotificationRecord posted : mNotificationList) {
1023 if (mNotificationsByKey.containsKey(posted.getKey())) {
1026 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
1036 void addNotification(NotificationRecord r) {
1037 mNotificationList.add(r);
1038 mNotificationsByKey.put(r.sbn.getKey(), r);
1039 if (r.sbn.isGroup()) {
1040 mSummaryByGroupKey.put(r.getGroupKey(), r);
1045 void addEnqueuedNotification(NotificationRecord r) {
1046 mEnqueuedNotifications.add(r);
1050 void setSystemReady(boolean systemReady) {
1051 mSystemReady = systemReady;
1055 void setHandler(Handler handler) {
1060 void setFallbackVibrationPattern(long[] vibrationPattern) {
1061 mFallbackVibrationPattern = vibrationPattern;
1065 void setPackageManager(IPackageManager packageManager) {
1066 mPackageManager = packageManager;
1070 void setRankingHelper(RankingHelper rankingHelper) {
1071 mRankingHelper = rankingHelper;
1075 void setIsTelevision(boolean isTelevision) {
1076 mIsTelevision = isTelevision;
1080 void setUsageStats(NotificationUsageStats us) {
1084 // TODO: All tests should use this init instead of the one-off setters above.
1086 void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
1087 LightsManager lightsManager, NotificationListeners notificationListeners,
1088 ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
1089 NotificationUsageStats usageStats) {
1090 Resources resources = getContext().getResources();
1091 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1092 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1093 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1095 mAm = ActivityManager.getService();
1096 mPackageManager = packageManager;
1097 mPackageManagerClient = packageManagerClient;
1098 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1099 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1100 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1101 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1102 mCompanionManager = companionManager;
1104 mHandler = new WorkerHandler(looper);
1105 mRankingThread.start();
1106 String[] extractorNames;
1108 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1109 } catch (Resources.NotFoundException e) {
1110 extractorNames = new String[0];
1112 mUsageStats = usageStats;
1113 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1114 mRankingHelper = new RankingHelper(getContext(),
1115 getContext().getPackageManager(),
1119 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
1120 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1121 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1123 public void onConfigChanged() {
1128 void onZenModeChanged() {
1129 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1130 getContext().sendBroadcastAsUser(
1131 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1132 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1133 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1134 synchronized (mNotificationLock) {
1135 updateInterruptionFilterLocked();
1140 void onPolicyChanged() {
1141 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1144 mSnoozeHelper = snoozeHelper;
1145 mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
1147 public void addAutoGroup(String key) {
1148 synchronized (mNotificationLock) {
1149 addAutogroupKeyLocked(key);
1151 mRankingHandler.requestSort(false);
1155 public void removeAutoGroup(String key) {
1156 synchronized (mNotificationLock) {
1157 removeAutogroupKeyLocked(key);
1159 mRankingHandler.requestSort(false);
1163 public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
1164 createAutoGroupSummary(userId, pkg, triggeringKey);
1168 public void removeAutoGroupSummary(int userId, String pkg) {
1169 synchronized (mNotificationLock) {
1170 clearAutogroupSummaryLocked(userId, pkg);
1175 final File systemDir = new File(Environment.getDataDirectory(), "system");
1176 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
1180 // This is a ManagedServices object that keeps track of the listeners.
1181 mListeners = notificationListeners;
1183 // This is a MangedServices object that keeps track of the assistant.
1184 mNotificationAssistants = new NotificationAssistants();
1186 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1187 if (mStatusBar != null) {
1188 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1191 mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1192 mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
1194 mFallbackVibrationPattern = getLongArray(resources,
1195 R.array.config_notificationFallbackVibePattern,
1196 VIBRATE_PATTERN_MAXLEN,
1197 DEFAULT_VIBRATE_PATTERN);
1199 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1201 // Don't start allowing notifications until the setup wizard has run once.
1202 // After that, including subsequent boots, init with notifications turned on.
1203 // This works on the first boot because the setup wizard will toggle this
1204 // flag at least once and we'll go back to 0 after that.
1205 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1206 Settings.Global.DEVICE_PROVISIONED, 0)) {
1207 mDisableNotificationEffects = true;
1209 mZenModeHelper.initZenMode();
1210 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1212 mUserProfiles.updateCache(getContext());
1213 listenForCallState();
1215 mSettingsObserver = new SettingsObserver(mHandler);
1217 mArchive = new Archive(resources.getInteger(
1218 R.integer.config_notificationServiceArchiveSize));
1220 mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
1221 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
1225 public void onStart() {
1226 SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
1228 public void repost(int userId, NotificationRecord r) {
1231 Slog.d(TAG, "Reposting " + r.getKey());
1233 enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
1234 r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
1235 r.sbn.getNotification(), userId);
1236 } catch (Exception e) {
1237 Slog.e(TAG, "Cannot un-snooze notification", e);
1242 init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
1243 getLocalService(LightsManager.class), new NotificationListeners(),
1244 null, snoozeHelper, new NotificationUsageStats(getContext()));
1246 // register for various Intents
1247 IntentFilter filter = new IntentFilter();
1248 filter.addAction(Intent.ACTION_SCREEN_ON);
1249 filter.addAction(Intent.ACTION_SCREEN_OFF);
1250 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1251 filter.addAction(Intent.ACTION_USER_PRESENT);
1252 filter.addAction(Intent.ACTION_USER_STOPPED);
1253 filter.addAction(Intent.ACTION_USER_SWITCHED);
1254 filter.addAction(Intent.ACTION_USER_ADDED);
1255 filter.addAction(Intent.ACTION_USER_REMOVED);
1256 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1257 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1258 getContext().registerReceiver(mIntentReceiver, filter);
1260 IntentFilter pkgFilter = new IntentFilter();
1261 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1262 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1263 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1264 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1265 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1266 pkgFilter.addDataScheme("package");
1267 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1270 IntentFilter suspendedPkgFilter = new IntentFilter();
1271 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1272 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1273 suspendedPkgFilter, null, null);
1275 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1276 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1279 IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
1280 timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
1281 getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
1283 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1284 publishLocalService(NotificationManagerInternal.class, mInternalService);
1287 private void sendRegisteredOnlyBroadcast(String action) {
1288 getContext().sendBroadcastAsUser(new Intent(action)
1289 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1293 public void onBootPhase(int phase) {
1294 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1295 // no beeping until we're basically done booting
1296 mSystemReady = true;
1298 // Grab our optional AudioService
1299 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1300 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1301 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1302 mZenModeHelper.onSystemReady();
1303 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1304 // This observer will force an update when observe is called, causing us to
1305 // bind to listener services.
1306 mSettingsObserver.observe();
1307 mListeners.onBootPhaseAppsCanStart();
1308 mNotificationAssistants.onBootPhaseAppsCanStart();
1309 mConditionProviders.onBootPhaseAppsCanStart();
1313 @GuardedBy("mNotificationLock")
1314 private void updateListenerHintsLocked() {
1315 final int hints = calculateHints();
1316 if (hints == mListenerHints) return;
1317 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1318 mListenerHints = hints;
1319 scheduleListenerHintsChanged(hints);
1322 @GuardedBy("mNotificationLock")
1323 private void updateEffectsSuppressorLocked() {
1324 final long updatedSuppressedEffects = calculateSuppressedEffects();
1325 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1326 final List<ComponentName> suppressors = getSuppressors();
1327 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1328 mEffectsSuppressors = suppressors;
1329 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1330 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1333 private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
1334 boolean fromListener) {
1335 if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
1337 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
1338 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1341 mRankingHelper.updateNotificationChannel(pkg, uid, channel);
1343 final NotificationChannel modifiedChannel =
1344 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
1346 if (!fromListener) {
1347 mListeners.notifyNotificationChannelChanged(
1348 pkg, UserHandle.getUserHandleForUid(uid),
1349 modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
1352 synchronized (mNotificationLock) {
1353 final int N = mNotificationList.size();
1354 for (int i = N - 1; i >= 0; --i) {
1355 NotificationRecord r = mNotificationList.get(i);
1356 if (r.sbn.getPackageName().equals(pkg)
1357 && r.sbn.getUid() == uid
1358 && channel.getId() != null
1359 && channel.getId().equals(r.getChannel().getId())) {
1360 r.updateNotificationChannel(modifiedChannel);
1364 mRankingHandler.requestSort(true);
1368 private ArrayList<ComponentName> getSuppressors() {
1369 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1370 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1371 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1373 for (ManagedServiceInfo info : serviceInfoList) {
1374 names.add(info.component);
1381 private boolean removeDisabledHints(ManagedServiceInfo info) {
1382 return removeDisabledHints(info, 0);
1385 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1386 boolean removed = false;
1388 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1389 final int hint = mListenersDisablingEffects.keyAt(i);
1390 final ArraySet<ManagedServiceInfo> listeners =
1391 mListenersDisablingEffects.valueAt(i);
1393 if (hints == 0 || (hint & hints) == hint) {
1394 removed = removed || listeners.remove(info);
1401 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1402 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1403 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1406 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1407 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1410 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1411 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1415 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1416 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1417 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1420 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1421 hintListeners.add(info);
1424 private int calculateHints() {
1426 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1427 int hint = mListenersDisablingEffects.keyAt(i);
1428 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1430 if (!serviceInfoList.isEmpty()) {
1438 private long calculateSuppressedEffects() {
1439 int hints = calculateHints();
1440 long suppressedEffects = 0;
1442 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1443 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1446 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1447 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1450 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1451 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1454 return suppressedEffects;
1457 @GuardedBy("mNotificationLock")
1458 private void updateInterruptionFilterLocked() {
1459 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1460 if (interruptionFilter == mInterruptionFilter) return;
1461 mInterruptionFilter = interruptionFilter;
1462 scheduleInterruptionFilterChanged(interruptionFilter);
1466 INotificationManager getBinderService() {
1467 return INotificationManager.Stub.asInterface(mService);
1471 NotificationManagerInternal getInternalService() {
1472 return mInternalService;
1475 private final IBinder mService = new INotificationManager.Stub() {
1477 // ============================================================================
1480 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1483 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1484 + " duration=" + duration);
1487 if (pkg == null || callback == null) {
1488 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1492 final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
1493 final boolean isPackageSuspended =
1494 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1496 if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
1497 (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
1498 || isPackageSuspended)) {
1499 Slog.e(TAG, "Suppressing toast from package " + pkg
1500 + (isPackageSuspended
1501 ? " due to package suspended by administrator."
1502 : " by user request."));
1506 synchronized (mToastQueue) {
1507 int callingPid = Binder.getCallingPid();
1508 long callingId = Binder.clearCallingIdentity();
1511 int index = indexOfToastLocked(pkg, callback);
1512 // If it's already in the queue, we update it in place, we don't
1513 // move it to the end of the queue.
1515 record = mToastQueue.get(index);
1516 record.update(duration);
1518 // Limit the number of toasts that any given package except the android
1519 // package can enqueue. Prevents DOS attacks and deals with leaks.
1520 if (!isSystemToast) {
1522 final int N = mToastQueue.size();
1523 for (int i=0; i<N; i++) {
1524 final ToastRecord r = mToastQueue.get(i);
1525 if (r.pkg.equals(pkg)) {
1527 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1528 Slog.e(TAG, "Package has already posted " + count
1529 + " toasts. Not showing more. Package=" + pkg);
1536 Binder token = new Binder();
1537 mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, DEFAULT_DISPLAY);
1538 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1539 mToastQueue.add(record);
1540 index = mToastQueue.size() - 1;
1541 keepProcessAliveIfNeededLocked(callingPid);
1543 // If it's at index 0, it's the current toast. It doesn't matter if it's
1544 // new or just been updated. Call back and tell it to show itself.
1545 // If the callback fails, this will remove it from the list, so don't
1546 // assume that it's valid after this.
1548 showNextToastLocked();
1551 Binder.restoreCallingIdentity(callingId);
1557 public void cancelToast(String pkg, ITransientNotification callback) {
1558 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1560 if (pkg == null || callback == null) {
1561 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1565 synchronized (mToastQueue) {
1566 long callingId = Binder.clearCallingIdentity();
1568 int index = indexOfToastLocked(pkg, callback);
1570 cancelToastLocked(index);
1572 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1573 + " callback=" + callback);
1576 Binder.restoreCallingIdentity(callingId);
1582 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1583 Notification notification, int userId) throws RemoteException {
1584 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1585 Binder.getCallingPid(), tag, id, notification, userId);
1589 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1590 checkCallerIsSystemOrSameApp(pkg);
1591 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1592 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1593 // Don't allow client applications to cancel foreground service notis or autobundled
1595 final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
1596 (Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
1597 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1598 mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
1602 public void cancelAllNotifications(String pkg, int userId) {
1603 checkCallerIsSystemOrSameApp(pkg);
1605 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1606 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1608 // Calling from user space, don't allow the canceling of actively
1609 // running foreground services.
1610 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1611 pkg, null, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1612 REASON_APP_CANCEL_ALL, null);
1616 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1617 checkCallerIsSystem();
1619 mRankingHelper.setEnabled(pkg, uid, enabled);
1620 // Now, cancel any outstanding notifications that are part of a just-disabled app
1622 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
1623 UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
1629 * Use this when you just want to know if notifications are OK for this package.
1632 public boolean areNotificationsEnabled(String pkg) {
1633 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1637 * Use this when you just want to know if notifications are OK for this package.
1640 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1641 checkCallerIsSystemOrSameApp(pkg);
1642 if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) {
1643 getContext().enforceCallingPermission(
1644 android.Manifest.permission.INTERACT_ACROSS_USERS,
1645 "canNotifyAsPackage for uid " + uid);
1648 return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
1652 public int getPackageImportance(String pkg) {
1653 checkCallerIsSystemOrSameApp(pkg);
1654 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1658 public boolean canShowBadge(String pkg, int uid) {
1659 checkCallerIsSystem();
1660 return mRankingHelper.canShowBadge(pkg, uid);
1664 public void setShowBadge(String pkg, int uid, boolean showBadge) {
1665 checkCallerIsSystem();
1666 mRankingHelper.setShowBadge(pkg, uid, showBadge);
1671 public void createNotificationChannelGroups(String pkg,
1672 ParceledListSlice channelGroupList) throws RemoteException {
1673 checkCallerIsSystemOrSameApp(pkg);
1674 List<NotificationChannelGroup> groups = channelGroupList.getList();
1675 final int groupSize = groups.size();
1676 for (int i = 0; i < groupSize; i++) {
1677 final NotificationChannelGroup group = groups.get(i);
1678 Preconditions.checkNotNull(group, "group in list is null");
1679 mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
1680 true /* fromTargetApp */);
1681 mListeners.notifyNotificationChannelGroupChanged(pkg,
1682 UserHandle.of(UserHandle.getCallingUserId()), group,
1683 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1688 private void createNotificationChannelsImpl(String pkg, int uid,
1689 ParceledListSlice channelsList) {
1690 List<NotificationChannel> channels = channelsList.getList();
1691 final int channelsSize = channels.size();
1692 for (int i = 0; i < channelsSize; i++) {
1693 final NotificationChannel channel = channels.get(i);
1694 Preconditions.checkNotNull(channel, "channel in list is null");
1695 mRankingHelper.createNotificationChannel(pkg, uid, channel,
1696 true /* fromTargetApp */);
1697 mListeners.notifyNotificationChannelChanged(pkg,
1698 UserHandle.getUserHandleForUid(uid),
1699 mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
1700 NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
1706 public void createNotificationChannels(String pkg,
1707 ParceledListSlice channelsList) throws RemoteException {
1708 checkCallerIsSystemOrSameApp(pkg);
1709 createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
1713 public void createNotificationChannelsForPackage(String pkg, int uid,
1714 ParceledListSlice channelsList) throws RemoteException {
1715 checkCallerIsSystem();
1716 createNotificationChannelsImpl(pkg, uid, channelsList);
1720 public NotificationChannel getNotificationChannel(String pkg, String channelId) {
1721 checkCallerIsSystemOrSameApp(pkg);
1722 return mRankingHelper.getNotificationChannel(
1723 pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
1727 public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
1728 String channelId, boolean includeDeleted) {
1729 checkCallerIsSystem();
1730 return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
1734 public void deleteNotificationChannel(String pkg, String channelId) {
1735 checkCallerIsSystemOrSameApp(pkg);
1736 final int callingUid = Binder.getCallingUid();
1737 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
1738 throw new IllegalArgumentException("Cannot delete default channel");
1740 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
1741 UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
1742 mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
1743 mListeners.notifyNotificationChannelChanged(pkg,
1744 UserHandle.getUserHandleForUid(callingUid),
1745 mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
1746 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1751 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
1753 checkCallerIsSystemOrSameApp(pkg);
1754 return new ParceledListSlice<>(new ArrayList(
1755 mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
1759 public void deleteNotificationChannelGroup(String pkg, String groupId) {
1760 checkCallerIsSystemOrSameApp(pkg);
1762 final int callingUid = Binder.getCallingUid();
1763 NotificationChannelGroup groupToDelete =
1764 mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
1765 if (groupToDelete != null) {
1766 List<NotificationChannel> deletedChannels =
1767 mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
1768 for (int i = 0; i < deletedChannels.size(); i++) {
1769 final NotificationChannel deletedChannel = deletedChannels.get(i);
1770 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
1772 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
1774 mListeners.notifyNotificationChannelChanged(pkg,
1775 UserHandle.getUserHandleForUid(callingUid),
1777 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1779 mListeners.notifyNotificationChannelGroupChanged(
1780 pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
1781 NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
1787 public void updateNotificationChannelForPackage(String pkg, int uid,
1788 NotificationChannel channel) {
1789 enforceSystemOrSystemUI("Caller not system or systemui");
1790 Preconditions.checkNotNull(channel);
1791 updateNotificationChannelInt(pkg, uid, channel, false);
1795 public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
1796 int uid, boolean includeDeleted) {
1797 enforceSystemOrSystemUI("getNotificationChannelsForPackage");
1798 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
1802 public int getNumNotificationChannelsForPackage(String pkg, int uid,
1803 boolean includeDeleted) {
1804 enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
1805 return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
1810 public boolean onlyHasDefaultChannel(String pkg, int uid) {
1811 enforceSystemOrSystemUI("onlyHasDefaultChannel");
1812 return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
1816 public int getDeletedChannelCount(String pkg, int uid) {
1817 enforceSystemOrSystemUI("getDeletedChannelCount");
1818 return mRankingHelper.getDeletedChannelCount(pkg, uid);
1822 public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
1823 String pkg, int uid, boolean includeDeleted) {
1824 checkCallerIsSystem();
1825 return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
1829 public NotificationChannelGroup getNotificationChannelGroupForPackage(
1830 String groupId, String pkg, int uid) {
1831 enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
1832 return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
1836 public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
1837 checkCallerIsSystemOrSameApp(pkg);
1838 return mRankingHelper.getNotificationChannels(
1839 pkg, Binder.getCallingUid(), false /* includeDeleted */);
1843 public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
1844 checkCallerIsSystem();
1846 // Cancel posted notifications
1847 cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
1848 UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
1850 // Listener & assistant
1851 mListeners.onPackagesChanged(true, new String[] {packageName});
1852 mNotificationAssistants.onPackagesChanged(true, new String[] {packageName});
1855 mConditionProviders.onPackagesChanged(true, new String[] {packageName});
1857 // Reset notification preferences
1859 mRankingHelper.onPackagesChanged(true, UserHandle.getCallingUserId(),
1860 new String[]{packageName}, new int[]{uid});
1868 * System-only API for getting a list of current (i.e. not cleared) notifications.
1870 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1871 * @returns A list of all the notifications, in natural order.
1874 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1875 // enforce() will ensure the calling uid has the correct permission
1876 getContext().enforceCallingOrSelfPermission(
1877 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1878 "NotificationManagerService.getActiveNotifications");
1880 StatusBarNotification[] tmp = null;
1881 int uid = Binder.getCallingUid();
1883 // noteOp will check to make sure the callingPkg matches the uid
1884 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1885 == AppOpsManager.MODE_ALLOWED) {
1886 synchronized (mNotificationLock) {
1887 tmp = new StatusBarNotification[mNotificationList.size()];
1888 final int N = mNotificationList.size();
1889 for (int i=0; i<N; i++) {
1890 tmp[i] = mNotificationList.get(i).sbn;
1898 * Public API for getting a list of current notifications for the calling package/uid.
1900 * Note that since notification posting is done asynchronously, this will not return
1901 * notifications that are in the process of being posted.
1903 * @returns A list of all the package's notifications, in natural order.
1906 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1907 int incomingUserId) {
1908 checkCallerIsSystemOrSameApp(pkg);
1909 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1910 Binder.getCallingUid(), incomingUserId, true, false,
1911 "getAppActiveNotifications", pkg);
1912 synchronized (mNotificationLock) {
1913 final ArrayMap<String, StatusBarNotification> map
1914 = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
1915 final int N = mNotificationList.size();
1916 for (int i = 0; i < N; i++) {
1917 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1918 mNotificationList.get(i).sbn);
1920 map.put(sbn.getKey(), sbn);
1923 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
1924 StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
1926 map.put(sbn.getKey(), sbn);
1929 final int M = mEnqueuedNotifications.size();
1930 for (int i = 0; i < M; i++) {
1931 StatusBarNotification sbn = sanitizeSbn(pkg, userId,
1932 mEnqueuedNotifications.get(i).sbn);
1934 map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
1937 final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
1938 list.addAll(map.values());
1939 return new ParceledListSlice<StatusBarNotification>(list);
1943 private StatusBarNotification sanitizeSbn(String pkg, int userId,
1944 StatusBarNotification sbn) {
1945 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1946 && (sbn.getNotification().flags
1947 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1948 // We could pass back a cloneLight() but clients might get confused and
1949 // try to send this thing back to notify() again, which would not work
1951 return new StatusBarNotification(
1952 sbn.getPackageName(),
1954 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1955 sbn.getNotification().clone(),
1956 sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
1962 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1964 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1967 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1968 // enforce() will ensure the calling uid has the correct permission
1969 getContext().enforceCallingOrSelfPermission(
1970 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1971 "NotificationManagerService.getHistoricalNotifications");
1973 StatusBarNotification[] tmp = null;
1974 int uid = Binder.getCallingUid();
1976 // noteOp will check to make sure the callingPkg matches the uid
1977 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1978 == AppOpsManager.MODE_ALLOWED) {
1979 synchronized (mArchive) {
1980 tmp = mArchive.getArray(count);
1987 * Register a listener binder directly with the notification manager.
1989 * Only works with system callers. Apps should extend
1990 * {@link android.service.notification.NotificationListenerService}.
1993 public void registerListener(final INotificationListener listener,
1994 final ComponentName component, final int userid) {
1995 enforceSystemOrSystemUI("INotificationManager.registerListener");
1996 mListeners.registerService(listener, component, userid);
2000 * Remove a listener binder directly
2003 public void unregisterListener(INotificationListener token, int userid) {
2004 mListeners.unregisterService(token, userid);
2008 * Allow an INotificationListener to simulate a "clear all" operation.
2010 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
2012 * @param token The binder for the listener, to check that the caller is allowed
2015 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
2016 final int callingUid = Binder.getCallingUid();
2017 final int callingPid = Binder.getCallingPid();
2018 long identity = Binder.clearCallingIdentity();
2020 synchronized (mNotificationLock) {
2021 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2023 final int N = keys.length;
2024 for (int i = 0; i < N; i++) {
2025 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2026 if (r == null) continue;
2027 final int userId = r.sbn.getUserId();
2028 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2029 !mUserProfiles.isCurrentProfile(userId)) {
2030 throw new SecurityException("Disallowed call from listener: "
2033 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2034 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
2038 cancelAllLocked(callingUid, callingPid, info.userid,
2039 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
2043 Binder.restoreCallingIdentity(identity);
2048 * Handle request from an approved listener to re-enable itself.
2050 * @param component The componenet to be re-enabled, caller must match package.
2053 public void requestBindListener(ComponentName component) {
2054 checkCallerIsSystemOrSameApp(component.getPackageName());
2055 long identity = Binder.clearCallingIdentity();
2057 ManagedServices manager =
2058 mNotificationAssistants.isComponentEnabledForCurrentProfiles(component)
2059 ? mNotificationAssistants
2061 manager.setComponentState(component, true);
2063 Binder.restoreCallingIdentity(identity);
2068 public void requestUnbindListener(INotificationListener token) {
2069 long identity = Binder.clearCallingIdentity();
2071 // allow bound services to disable themselves
2072 synchronized (mNotificationLock) {
2073 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2074 info.getOwner().setComponentState(info.component, false);
2077 Binder.restoreCallingIdentity(identity);
2082 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
2083 long identity = Binder.clearCallingIdentity();
2085 synchronized (mNotificationLock) {
2086 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2088 final int N = keys.length;
2089 for (int i = 0; i < N; i++) {
2090 NotificationRecord r = mNotificationsByKey.get(keys[i]);
2091 if (r == null) continue;
2092 final int userId = r.sbn.getUserId();
2093 if (userId != info.userid && userId != UserHandle.USER_ALL &&
2094 !mUserProfiles.isCurrentProfile(userId)) {
2095 throw new SecurityException("Disallowed call from listener: "
2099 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
2100 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
2101 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
2103 UsageEvents.Event.USER_INTERACTION);
2110 Binder.restoreCallingIdentity(identity);
2115 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2117 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2119 * @param info The binder for the listener, to check that the caller is allowed
2121 @GuardedBy("mNotificationLock")
2122 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
2123 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
2124 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
2125 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
2127 userId, REASON_LISTENER_CANCEL, info);
2131 * Allow an INotificationListener to snooze a single notification until a context.
2133 * @param token The binder for the listener, to check that the caller is allowed
2136 public void snoozeNotificationUntilContextFromListener(INotificationListener token,
2137 String key, String snoozeCriterionId) {
2138 long identity = Binder.clearCallingIdentity();
2140 synchronized (mNotificationLock) {
2141 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2142 snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
2145 Binder.restoreCallingIdentity(identity);
2150 * Allow an INotificationListener to snooze a single notification until a time.
2152 * @param token The binder for the listener, to check that the caller is allowed
2155 public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
2157 long identity = Binder.clearCallingIdentity();
2159 synchronized (mNotificationLock) {
2160 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2161 snoozeNotificationInt(key, duration, null, info);
2164 Binder.restoreCallingIdentity(identity);
2169 * Allows the notification assistant to un-snooze a single notification.
2171 * @param token The binder for the assistant, to check that the caller is allowed
2174 public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
2175 long identity = Binder.clearCallingIdentity();
2177 synchronized (mNotificationLock) {
2178 final ManagedServiceInfo info =
2179 mNotificationAssistants.checkServiceTokenLocked(token);
2180 unsnoozeNotificationInt(key, info);
2183 Binder.restoreCallingIdentity(identity);
2188 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
2190 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
2192 * @param token The binder for the listener, to check that the caller is allowed
2195 public void cancelNotificationFromListener(INotificationListener token, String pkg,
2196 String tag, int id) {
2197 final int callingUid = Binder.getCallingUid();
2198 final int callingPid = Binder.getCallingPid();
2199 long identity = Binder.clearCallingIdentity();
2201 synchronized (mNotificationLock) {
2202 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2203 if (info.supportsProfiles()) {
2204 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
2205 + "from " + info.component
2206 + " use cancelNotification(key) instead.");
2208 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
2209 pkg, tag, id, info.userid);
2213 Binder.restoreCallingIdentity(identity);
2218 * Allow an INotificationListener to request the list of outstanding notifications seen by
2219 * the current user. Useful when starting up, after which point the listener callbacks
2222 * @param token The binder for the listener, to check that the caller is allowed
2223 * @param keys An array of notification keys to fetch, or null to fetch everything
2224 * @returns The return value will contain the notifications specified in keys, in that
2225 * order, or if keys is null, all the notifications, in natural order.
2228 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
2229 INotificationListener token, String[] keys, int trim) {
2230 synchronized (mNotificationLock) {
2231 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2232 final boolean getKeys = keys != null;
2233 final int N = getKeys ? keys.length : mNotificationList.size();
2234 final ArrayList<StatusBarNotification> list
2235 = new ArrayList<StatusBarNotification>(N);
2236 for (int i=0; i<N; i++) {
2237 final NotificationRecord r = getKeys
2238 ? mNotificationsByKey.get(keys[i])
2239 : mNotificationList.get(i);
2240 if (r == null) continue;
2241 StatusBarNotification sbn = r.sbn;
2242 if (!isVisibleToListener(sbn, info)) continue;
2243 StatusBarNotification sbnToSend =
2244 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2245 list.add(sbnToSend);
2247 return new ParceledListSlice<StatusBarNotification>(list);
2252 * Allow an INotificationListener to request the list of outstanding snoozed notifications
2253 * seen by the current user. Useful when starting up, after which point the listener
2254 * callbacks should be used.
2256 * @param token The binder for the listener, to check that the caller is allowed
2257 * @returns The return value will contain the notifications specified in keys, in that
2258 * order, or if keys is null, all the notifications, in natural order.
2261 public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
2262 INotificationListener token, int trim) {
2263 synchronized (mNotificationLock) {
2264 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2265 List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
2266 final int N = snoozedRecords.size();
2267 final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
2268 for (int i=0; i < N; i++) {
2269 final NotificationRecord r = snoozedRecords.get(i);
2270 if (r == null) continue;
2271 StatusBarNotification sbn = r.sbn;
2272 if (!isVisibleToListener(sbn, info)) continue;
2273 StatusBarNotification sbnToSend =
2274 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
2275 list.add(sbnToSend);
2277 return new ParceledListSlice<>(list);
2282 public void requestHintsFromListener(INotificationListener token, int hints) {
2283 final long identity = Binder.clearCallingIdentity();
2285 synchronized (mNotificationLock) {
2286 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2287 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
2288 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
2289 | HINT_HOST_DISABLE_CALL_EFFECTS;
2290 final boolean disableEffects = (hints & disableEffectsMask) != 0;
2291 if (disableEffects) {
2292 addDisabledHints(info, hints);
2294 removeDisabledHints(info, hints);
2296 updateListenerHintsLocked();
2297 updateEffectsSuppressorLocked();
2300 Binder.restoreCallingIdentity(identity);
2305 public int getHintsFromListener(INotificationListener token) {
2306 synchronized (mNotificationLock) {
2307 return mListenerHints;
2312 public void requestInterruptionFilterFromListener(INotificationListener token,
2313 int interruptionFilter) throws RemoteException {
2314 final long identity = Binder.clearCallingIdentity();
2316 synchronized (mNotificationLock) {
2317 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2318 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
2319 updateInterruptionFilterLocked();
2322 Binder.restoreCallingIdentity(identity);
2327 public int getInterruptionFilterFromListener(INotificationListener token)
2328 throws RemoteException {
2329 synchronized (mNotificationLight) {
2330 return mInterruptionFilter;
2335 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
2336 throws RemoteException {
2337 synchronized (mNotificationLock) {
2338 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
2339 if (info == null) return;
2340 mListeners.setOnNotificationPostedTrimLocked(info, trim);
2345 public int getZenMode() {
2346 return mZenModeHelper.getZenMode();
2350 public ZenModeConfig getZenModeConfig() {
2351 enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
2352 return mZenModeHelper.getConfig();
2356 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
2357 enforceSystemOrSystemUI("INotificationManager.setZenMode");
2358 final long identity = Binder.clearCallingIdentity();
2360 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
2362 Binder.restoreCallingIdentity(identity);
2367 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2368 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2369 return mZenModeHelper.getZenRules();
2373 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2374 Preconditions.checkNotNull(id, "Id is null");
2375 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2376 return mZenModeHelper.getAutomaticZenRule(id);
2380 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2381 throws RemoteException {
2382 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2383 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2384 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2385 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2386 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2388 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2389 "addAutomaticZenRule");
2393 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2394 throws RemoteException {
2395 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2396 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2397 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2398 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2399 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2401 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2402 "updateAutomaticZenRule");
2406 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2407 Preconditions.checkNotNull(id, "Id is null");
2408 // Verify that they can modify zen rules.
2409 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2411 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2415 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2416 Preconditions.checkNotNull(packageName, "Package name is null");
2417 enforceSystemOrSystemUI("removeAutomaticZenRules");
2419 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2423 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2424 Preconditions.checkNotNull(owner, "Owner is null");
2425 enforceSystemOrSystemUI("getRuleInstanceCount");
2427 return mZenModeHelper.getCurrentInstanceCount(owner);
2431 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2432 enforcePolicyAccess(pkg, "setInterruptionFilter");
2433 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2434 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2435 final long identity = Binder.clearCallingIdentity();
2437 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
2439 Binder.restoreCallingIdentity(identity);
2444 public void notifyConditions(final String pkg, IConditionProvider provider,
2445 final Condition[] conditions) {
2446 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2447 checkCallerIsSystemOrSameApp(pkg);
2448 mHandler.post(new Runnable() {
2451 mConditionProviders.notifyConditions(pkg, info, conditions);
2457 public void requestUnbindProvider(IConditionProvider provider) {
2458 long identity = Binder.clearCallingIdentity();
2460 // allow bound services to disable themselves
2461 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2462 info.getOwner().setComponentState(info.component, false);
2464 Binder.restoreCallingIdentity(identity);
2469 public void requestBindProvider(ComponentName component) {
2470 checkCallerIsSystemOrSameApp(component.getPackageName());
2471 long identity = Binder.clearCallingIdentity();
2473 mConditionProviders.setComponentState(component, true);
2475 Binder.restoreCallingIdentity(identity);
2479 private void enforceSystemOrSystemUI(String message) {
2480 if (isCallerSystemOrPhone()) return;
2481 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2485 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2487 checkCallerIsSystemOrSameApp(pkg);
2488 } catch (SecurityException e) {
2489 getContext().enforceCallingPermission(
2490 android.Manifest.permission.STATUS_BAR_SERVICE,
2495 private void enforcePolicyAccess(int uid, String method) {
2496 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2497 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2500 boolean accessAllowed = false;
2501 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2502 final int packageCount = packages.length;
2503 for (int i = 0; i < packageCount; i++) {
2504 if (checkPolicyAccess(packages[i])) {
2505 accessAllowed = true;
2508 if (!accessAllowed) {
2509 Slog.w(TAG, "Notification policy access denied calling " + method);
2510 throw new SecurityException("Notification policy access denied");
2514 private void enforcePolicyAccess(String pkg, String method) {
2515 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2516 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2519 checkCallerIsSameApp(pkg);
2520 if (!checkPolicyAccess(pkg)) {
2521 Slog.w(TAG, "Notification policy access denied calling " + method);
2522 throw new SecurityException("Notification policy access denied");
2526 private boolean checkPackagePolicyAccess(String pkg) {
2527 return mPolicyAccess.isPackageGranted(pkg);
2530 private boolean checkPolicyAccess(String pkg) {
2532 int uid = getContext().getPackageManager().getPackageUidAsUser(
2533 pkg, UserHandle.getCallingUserId());
2534 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2535 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2539 } catch (NameNotFoundException e) {
2542 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2546 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2547 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2548 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2549 if (filter != null && filter.stats) {
2550 dumpJson(pw, filter);
2551 } else if (filter != null && filter.proto) {
2552 dumpProto(fd, filter);
2554 dumpImpl(pw, filter);
2559 public ComponentName getEffectsSuppressor() {
2560 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2564 public boolean matchesCallFilter(Bundle extras) {
2565 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2566 return mZenModeHelper.matchesCallFilter(
2567 Binder.getCallingUserHandle(),
2569 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2570 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2571 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2575 public boolean isSystemConditionProviderEnabled(String path) {
2576 enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
2577 return mConditionProviders.isSystemProviderEnabled(path);
2580 // Backup/restore interface
2582 public byte[] getBackupPayload(int user) {
2583 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2584 //TODO: http://b/22388012
2585 if (user != UserHandle.USER_SYSTEM) {
2586 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2589 synchronized(mPolicyFile) {
2590 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2592 writePolicyXml(baos, true /*forBackup*/);
2593 return baos.toByteArray();
2594 } catch (IOException e) {
2595 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2602 public void applyRestore(byte[] payload, int user) {
2603 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2604 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2605 if (payload == null) {
2606 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2609 //TODO: http://b/22388012
2610 if (user != UserHandle.USER_SYSTEM) {
2611 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2614 synchronized(mPolicyFile) {
2615 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2617 readPolicyXml(bais, true /*forRestore*/);
2619 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2620 Slog.w(TAG, "applyRestore: error reading payload", e);
2626 public boolean isNotificationPolicyAccessGranted(String pkg) {
2627 return checkPolicyAccess(pkg);
2631 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2632 enforceSystemOrSystemUIOrSamePackage(pkg,
2633 "request policy access status for another package");
2634 return checkPolicyAccess(pkg);
2638 public String[] getPackagesRequestingNotificationPolicyAccess()
2639 throws RemoteException {
2640 enforceSystemOrSystemUI("request policy access packages");
2641 final long identity = Binder.clearCallingIdentity();
2643 return mPolicyAccess.getRequestingPackages();
2645 Binder.restoreCallingIdentity(identity);
2650 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2651 throws RemoteException {
2652 enforceSystemOrSystemUI("grant notification policy access");
2653 final long identity = Binder.clearCallingIdentity();
2655 synchronized (mNotificationLock) {
2656 mPolicyAccess.put(pkg, granted);
2659 Binder.restoreCallingIdentity(identity);
2664 public Policy getNotificationPolicy(String pkg) {
2665 enforcePolicyAccess(pkg, "getNotificationPolicy");
2666 final long identity = Binder.clearCallingIdentity();
2668 return mZenModeHelper.getNotificationPolicy();
2670 Binder.restoreCallingIdentity(identity);
2675 public void setNotificationPolicy(String pkg, Policy policy) {
2676 enforcePolicyAccess(pkg, "setNotificationPolicy");
2677 final long identity = Binder.clearCallingIdentity();
2679 mZenModeHelper.setNotificationPolicy(policy);
2681 Binder.restoreCallingIdentity(identity);
2686 public void applyEnqueuedAdjustmentFromAssistant(INotificationListener token,
2687 Adjustment adjustment) throws RemoteException {
2688 final long identity = Binder.clearCallingIdentity();
2690 synchronized (mNotificationLock) {
2691 mNotificationAssistants.checkServiceTokenLocked(token);
2692 int N = mEnqueuedNotifications.size();
2693 for (int i = 0; i < N; i++) {
2694 final NotificationRecord n = mEnqueuedNotifications.get(i);
2695 if (Objects.equals(adjustment.getKey(), n.getKey())
2696 && Objects.equals(adjustment.getUser(), n.getUserId())) {
2697 applyAdjustment(n, adjustment);
2703 Binder.restoreCallingIdentity(identity);
2708 public void applyAdjustmentFromAssistant(INotificationListener token,
2709 Adjustment adjustment) throws RemoteException {
2710 final long identity = Binder.clearCallingIdentity();
2712 synchronized (mNotificationLock) {
2713 mNotificationAssistants.checkServiceTokenLocked(token);
2714 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2715 applyAdjustment(n, adjustment);
2717 mRankingHandler.requestSort(true);
2719 Binder.restoreCallingIdentity(identity);
2724 public void applyAdjustmentsFromAssistant(INotificationListener token,
2725 List<Adjustment> adjustments) throws RemoteException {
2727 final long identity = Binder.clearCallingIdentity();
2729 synchronized (mNotificationLock) {
2730 mNotificationAssistants.checkServiceTokenLocked(token);
2731 for (Adjustment adjustment : adjustments) {
2732 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2733 applyAdjustment(n, adjustment);
2736 mRankingHandler.requestSort(true);
2738 Binder.restoreCallingIdentity(identity);
2743 public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
2744 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
2745 Preconditions.checkNotNull(channel);
2746 Preconditions.checkNotNull(pkg);
2747 Preconditions.checkNotNull(user);
2749 verifyPrivilegedListener(token, user);
2750 updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
2754 public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
2755 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2756 Preconditions.checkNotNull(pkg);
2757 Preconditions.checkNotNull(user);
2758 verifyPrivilegedListener(token, user);
2760 return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
2761 false /* includeDeleted */);
2765 public ParceledListSlice<NotificationChannelGroup>
2766 getNotificationChannelGroupsFromPrivilegedListener(
2767 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
2768 Preconditions.checkNotNull(pkg);
2769 Preconditions.checkNotNull(user);
2770 verifyPrivilegedListener(token, user);
2772 List<NotificationChannelGroup> groups = new ArrayList<>();
2773 groups.addAll(mRankingHelper.getNotificationChannelGroups(
2774 pkg, getUidForPackageAndUser(pkg, user)));
2775 return new ParceledListSlice<>(groups);
2778 private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
2779 ManagedServiceInfo info;
2780 synchronized (mNotificationLock) {
2781 info = mListeners.checkServiceTokenLocked(token);
2783 if (!hasCompanionDevice(info)) {
2784 throw new SecurityException(info + " does not have access");
2786 if (!info.enabledAndUserMatches(user.getIdentifier())) {
2787 throw new SecurityException(info + " does not have access");
2791 private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
2793 long identity = Binder.clearCallingIdentity();
2795 uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
2797 Binder.restoreCallingIdentity(identity);
2803 private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
2807 if (adjustment.getSignals() != null) {
2808 Bundle.setDefusable(adjustment.getSignals(), true);
2809 final ArrayList<String> people =
2810 adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
2811 final ArrayList<SnoozeCriterion> snoozeCriterionList =
2812 adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
2813 n.setPeopleOverride(people);
2814 n.setSnoozeCriteria(snoozeCriterionList);
2818 @GuardedBy("mNotificationLock")
2819 private void addAutogroupKeyLocked(String key) {
2820 NotificationRecord n = mNotificationsByKey.get(key);
2824 n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
2825 EventLogTags.writeNotificationAutogrouped(key);
2828 @GuardedBy("mNotificationLock")
2829 private void removeAutogroupKeyLocked(String key) {
2830 NotificationRecord n = mNotificationsByKey.get(key);
2834 n.setOverrideGroupKey(null);
2835 EventLogTags.writeNotificationUnautogrouped(key);
2838 // Clears the 'fake' auto-group summary.
2839 @GuardedBy("mNotificationLock")
2840 private void clearAutogroupSummaryLocked(int userId, String pkg) {
2841 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2842 if (summaries != null && summaries.containsKey(pkg)) {
2844 final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
2845 if (removed != null) {
2846 boolean wasPosted = removeFromNotificationListsLocked(removed);
2847 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
2852 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2853 private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
2854 NotificationRecord summaryRecord = null;
2855 synchronized (mNotificationLock) {
2856 NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
2857 if (notificationRecord == null) {
2858 // The notification could have been cancelled again already. A successive
2859 // adjustment will post a summary if needed.
2862 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2863 userId = adjustedSbn.getUser().getIdentifier();
2864 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2865 if (summaries == null) {
2866 summaries = new ArrayMap<>();
2868 mAutobundledSummaries.put(userId, summaries);
2869 if (!summaries.containsKey(pkg)) {
2871 final ApplicationInfo appInfo =
2872 adjustedSbn.getNotification().extras.getParcelable(
2873 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2874 final Bundle extras = new Bundle();
2875 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2876 final String channelId = notificationRecord.getChannel().getId();
2877 final Notification summaryNotification =
2878 new Notification.Builder(getContext(), channelId)
2879 .setSmallIcon(adjustedSbn.getNotification().getSmallIcon())
2880 .setGroupSummary(true)
2881 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
2882 .setGroup(GroupHelper.AUTOGROUP_KEY)
2883 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2884 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2885 .setColor(adjustedSbn.getNotification().color)
2888 summaryNotification.extras.putAll(extras);
2889 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
2890 if (appIntent != null) {
2891 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2892 getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
2894 final StatusBarNotification summarySbn =
2895 new StatusBarNotification(adjustedSbn.getPackageName(),
2896 adjustedSbn.getOpPkg(),
2898 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
2899 adjustedSbn.getInitialPid(), summaryNotification,
2900 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
2901 System.currentTimeMillis());
2902 summaryRecord = new NotificationRecord(getContext(), summarySbn,
2903 notificationRecord.getChannel());
2904 summaries.put(pkg, summarySbn.getKey());
2907 if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
2908 summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord)) {
2909 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2913 private String disableNotificationEffects(NotificationRecord record) {
2914 if (mDisableNotificationEffects) {
2915 return "booleanState";
2917 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2918 return "listenerHints";
2920 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2926 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2927 JSONObject dump = new JSONObject();
2929 dump.put("service", "Notification Manager");
2930 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2931 dump.put("ranking", mRankingHelper.dumpJson(filter));
2932 dump.put("stats", mUsageStats.dumpJson(filter));
2933 dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
2934 } catch (JSONException e) {
2935 e.printStackTrace();
2940 private void dumpProto(FileDescriptor fd, DumpFilter filter) {
2941 final ProtoOutputStream proto = new ProtoOutputStream(fd);
2942 synchronized (mNotificationLock) {
2943 long records = proto.start(NotificationServiceDumpProto.RECORDS);
2944 int N = mNotificationList.size();
2946 for (int i = 0; i < N; i++) {
2947 final NotificationRecord nr = mNotificationList.get(i);
2948 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2949 nr.dump(proto, filter.redact);
2950 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.POSTED);
2953 N = mEnqueuedNotifications.size();
2955 for (int i = 0; i < N; i++) {
2956 final NotificationRecord nr = mEnqueuedNotifications.get(i);
2957 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2958 nr.dump(proto, filter.redact);
2959 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.ENQUEUED);
2962 List<NotificationRecord> snoozed = mSnoozeHelper.getSnoozed();
2965 for (int i = 0; i < N; i++) {
2966 final NotificationRecord nr = snoozed.get(i);
2967 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2968 nr.dump(proto, filter.redact);
2969 proto.write(NotificationRecordProto.STATE, NotificationServiceProto.SNOOZED);
2975 long zenLog = proto.start(NotificationServiceDumpProto.ZEN);
2976 mZenModeHelper.dump(proto);
2977 for (ComponentName suppressor : mEffectsSuppressors) {
2978 proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString());
2985 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2986 pw.print("Current Notification Manager state");
2987 if (filter.filtered) {
2988 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2992 final boolean zenOnly = filter.filtered && filter.zen;
2995 synchronized (mToastQueue) {
2996 N = mToastQueue.size();
2998 pw.println(" Toast Queue:");
2999 for (int i=0; i<N; i++) {
3000 mToastQueue.get(i).dump(pw, " ", filter);
3007 synchronized (mNotificationLock) {
3009 N = mNotificationList.size();
3011 pw.println(" Notification List:");
3012 for (int i=0; i<N; i++) {
3013 final NotificationRecord nr = mNotificationList.get(i);
3014 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3015 nr.dump(pw, " ", getContext(), filter.redact);
3020 if (!filter.filtered) {
3023 pw.println(" Lights List:");
3024 for (int i=0; i<N; i++) {
3030 pw.println(mLights.get(i));
3034 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
3035 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
3036 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
3037 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
3038 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
3039 pw.println(" mCallState=" + callStateToString(mCallState));
3040 pw.println(" mSystemReady=" + mSystemReady);
3041 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
3043 pw.println(" mArchive=" + mArchive.toString());
3044 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
3046 while (iter.hasNext()) {
3047 final StatusBarNotification sbn = iter.next();
3048 if (filter != null && !filter.matches(sbn)) continue;
3049 pw.println(" " + sbn);
3051 if (iter.hasNext()) pw.println(" ...");
3057 N = mEnqueuedNotifications.size();
3059 pw.println(" Enqueued Notification List:");
3060 for (int i = 0; i < N; i++) {
3061 final NotificationRecord nr = mEnqueuedNotifications.get(i);
3062 if (filter.filtered && !filter.matches(nr.sbn)) continue;
3063 nr.dump(pw, " ", getContext(), filter.redact);
3068 mSnoozeHelper.dump(pw, filter);
3073 pw.println("\n Ranking Config:");
3074 mRankingHelper.dump(pw, " ", filter);
3076 pw.println("\n Notification listeners:");
3077 mListeners.dump(pw, filter);
3078 pw.print(" mListenerHints: "); pw.println(mListenerHints);
3079 pw.print(" mListenersDisablingEffects: (");
3080 N = mListenersDisablingEffects.size();
3081 for (int i = 0; i < N; i++) {
3082 final int hint = mListenersDisablingEffects.keyAt(i);
3083 if (i > 0) pw.print(';');
3084 pw.print("hint[" + hint + "]:");
3086 final ArraySet<ManagedServiceInfo> listeners =
3087 mListenersDisablingEffects.valueAt(i);
3088 final int listenerSize = listeners.size();
3090 for (int j = 0; j < listenerSize; j++) {
3091 if (i > 0) pw.print(',');
3092 final ManagedServiceInfo listener = listeners.valueAt(i);
3093 pw.print(listener.component);
3097 pw.println("\n Notification assistant services:");
3098 mNotificationAssistants.dump(pw, filter);
3101 if (!filter.filtered || zenOnly) {
3102 pw.println("\n Zen Mode:");
3103 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
3104 mZenModeHelper.dump(pw, " ");
3106 pw.println("\n Zen Log:");
3107 ZenLog.dump(pw, " ");
3110 pw.println("\n Policy access:");
3111 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
3113 pw.println("\n Condition providers:");
3114 mConditionProviders.dump(pw, filter);
3116 pw.println("\n Group summaries:");
3117 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
3118 NotificationRecord r = entry.getValue();
3119 pw.println(" " + entry.getKey() + " -> " + r.getKey());
3120 if (mNotificationsByKey.get(r.getKey()) != r) {
3121 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
3122 r.dump(pw, " ", getContext(), filter.redact);
3127 pw.println("\n Usage Stats:");
3128 mUsageStats.dump(pw, " ", filter);
3134 * The private API only accessible to the system process.
3136 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
3138 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
3139 String tag, int id, Notification notification, int userId) {
3140 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
3145 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
3147 checkCallerIsSystem();
3148 mHandler.post(new Runnable() {
3151 synchronized (mNotificationLock) {
3152 removeForegroundServiceFlagByListLocked(
3153 mEnqueuedNotifications, pkg, notificationId, userId);
3154 removeForegroundServiceFlagByListLocked(
3155 mNotificationList, pkg, notificationId, userId);
3161 @GuardedBy("mNotificationLock")
3162 private void removeForegroundServiceFlagByListLocked(
3163 ArrayList<NotificationRecord> notificationList, String pkg, int notificationId,
3165 NotificationRecord r = findNotificationByListLocked(
3166 notificationList, pkg, null, notificationId, userId);
3170 StatusBarNotification sbn = r.sbn;
3171 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
3172 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
3173 // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
3174 // initially *and* force remove FLAG_FOREGROUND_SERVICE.
3175 sbn.getNotification().flags =
3176 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
3177 mRankingHelper.sort(mNotificationList);
3178 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
3179 mGroupHelper.onNotificationPosted(sbn);
3183 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3184 final int callingPid, final String tag, final int id, final Notification notification,
3185 int incomingUserId) {
3187 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3188 + " notification=" + notification);
3190 checkCallerIsSystemOrSameApp(pkg);
3192 final int userId = ActivityManager.handleIncomingUser(callingPid,
3193 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3194 final UserHandle user = new UserHandle(userId);
3196 if (pkg == null || notification == null) {
3197 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3198 + " id=" + id + " notification=" + notification);
3201 // The system can post notifications for any package, let us resolve that.
3202 final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3204 // Fix the notification as best we can.
3206 final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3207 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3208 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3209 Notification.addFieldsFromContext(ai, notification);
3211 boolean canColorize = PERMISSION_GRANTED == mPackageManagerClient.checkPermission(
3212 "com.android.permission.USE_COLORIZED_NOTIFICATIONS", pkg);
3214 notification.flags |= Notification.FLAG_CAN_COLORIZE;
3216 notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3219 } catch (NameNotFoundException e) {
3220 Slog.e(TAG, "Cannot create a context for sending app", e);
3224 mUsageStats.registerEnqueuedByApp(pkg);
3226 // setup local book-keeping
3227 String channelId = notification.getChannelId();
3228 if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3229 channelId = (new Notification.TvExtender(notification)).getChannelId();
3231 final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3232 notificationUid, channelId, false /* includeDeleted */);
3233 if (channel == null) {
3234 final String noChannelStr = "No Channel found for "
3236 + ", channelId=" + channelId
3239 + ", opPkg=" + opPkg
3240 + ", callingUid=" + callingUid
3241 + ", userId=" + userId
3242 + ", incomingUserId=" + incomingUserId
3243 + ", notificationUid=" + notificationUid
3244 + ", notification=" + notification;
3245 Log.e(TAG, noChannelStr);
3246 doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3247 "Failed to post notification on channel \"" + channelId + "\"\n" +
3248 "See log for more details");
3252 final StatusBarNotification n = new StatusBarNotification(
3253 pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3254 user, null, System.currentTimeMillis());
3255 final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3257 if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
3261 // Whitelist pending intents.
3262 if (notification.allPendingIntents != null) {
3263 final int intentCount = notification.allPendingIntents.size();
3264 if (intentCount > 0) {
3265 final ActivityManagerInternal am = LocalServices
3266 .getService(ActivityManagerInternal.class);
3267 final long duration = LocalServices.getService(
3268 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3269 for (int i = 0; i < intentCount; i++) {
3270 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3271 if (pendingIntent != null) {
3272 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3273 WHITELIST_TOKEN, duration);
3279 mHandler.post(new EnqueueNotificationRunnable(userId, r));
3282 private void doChannelWarningToast(CharSequence toastText) {
3283 final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
3284 final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
3285 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
3286 if (warningEnabled) {
3287 Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
3288 Toast.LENGTH_SHORT);
3293 private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
3294 // The system can post notifications on behalf of any package it wants
3295 if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
3297 return getContext().getPackageManager()
3298 .getPackageUidAsUser(opPackageName, userId);
3299 } catch (NameNotFoundException e) {
3307 * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
3311 private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
3312 NotificationRecord r) {
3313 final String pkg = r.sbn.getPackageName();
3314 final String dialerPackage =
3315 getContext().getSystemService(TelecomManager.class).getSystemDialerPackage();
3316 final boolean isSystemNotification =
3317 isUidSystemOrPhone(callingUid) || ("android".equals(pkg))
3318 || TextUtils.equals(pkg, dialerPackage);
3319 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
3321 // Limit the number of notifications that any given package except the android
3322 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
3323 if (!isSystemNotification && !isNotificationFromListener) {
3324 synchronized (mNotificationLock) {
3325 if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
3326 // Ephemeral apps have some special constraints for notifications.
3327 // They are not allowed to create new notifications however they are allowed to
3328 // update notifications created by the system (e.g. a foreground service
3330 throw new SecurityException("Instant app " + pkg
3331 + " cannot create notifications");
3334 // rate limit updates that aren't completed progress notifications
3335 if (mNotificationsByKey.get(r.sbn.getKey()) != null
3336 && !r.getNotification().hasCompletedProgress()) {
3338 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
3339 if (appEnqueueRate > mMaxPackageEnqueueRate) {
3340 mUsageStats.registerOverRateQuota(pkg);
3341 final long now = SystemClock.elapsedRealtime();
3342 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
3343 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
3344 + ". Shedding events. package=" + pkg);
3345 mLastOverRateLogTime = now;
3351 // limit the number of outstanding notificationrecords an app can have
3352 int count = getNotificationCountLocked(pkg, userId, id, tag);
3353 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
3354 mUsageStats.registerOverCountQuota(pkg);
3355 Slog.e(TAG, "Package has already posted or enqueued " + count
3356 + " notifications. Not showing more. package=" + pkg);
3363 if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
3364 MetricsLogger.action(r.getLogMaker()
3365 .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
3366 .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
3368 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
3370 mSnoozeHelper.update(userId, r);
3377 if (isBlocked(r, mUsageStats)) {
3384 protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
3385 String excludedTag) {
3387 final int N = mNotificationList.size();
3388 for (int i = 0; i < N; i++) {
3389 final NotificationRecord existing = mNotificationList.get(i);
3390 if (existing.sbn.getPackageName().equals(pkg)
3391 && existing.sbn.getUserId() == userId) {
3392 if (existing.sbn.getId() == excludedId
3393 && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
3399 final int M = mEnqueuedNotifications.size();
3400 for (int i = 0; i < M; i++) {
3401 final NotificationRecord existing = mEnqueuedNotifications.get(i);
3402 if (existing.sbn.getPackageName().equals(pkg)
3403 && existing.sbn.getUserId() == userId) {
3410 protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
3411 final String pkg = r.sbn.getPackageName();
3412 final int callingUid = r.sbn.getUid();
3414 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
3415 if (isPackageSuspended) {
3416 Slog.e(TAG, "Suppressing notification from package due to package "
3417 + "suspended by administrator.");
3418 usageStats.registerSuspendedByAdmin(r);
3419 return isPackageSuspended;
3422 final boolean isBlocked =
3423 mRankingHelper.getImportance(pkg, callingUid) == NotificationManager.IMPORTANCE_NONE
3424 || r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
3426 Slog.e(TAG, "Suppressing notification from package by user request.");
3427 usageStats.registerBlocked(r);
3432 protected class SnoozeNotificationRunnable implements Runnable {
3433 private final String mKey;
3434 private final long mDuration;
3435 private final String mSnoozeCriterionId;
3437 SnoozeNotificationRunnable(String key, long duration, String snoozeCriterionId) {
3439 mDuration = duration;
3440 mSnoozeCriterionId = snoozeCriterionId;
3445 synchronized (mNotificationLock) {
3446 final NotificationRecord r = findNotificationByKeyLocked(mKey);
3453 @GuardedBy("mNotificationLock")
3454 void snoozeLocked(NotificationRecord r) {
3455 if (r.sbn.isGroup()) {
3456 final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked(
3457 r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
3458 if (r.getNotification().isGroupSummary()) {
3459 // snooze summary and all children
3460 for (int i = 0; i < groupNotifications.size(); i++) {
3461 snoozeNotificationLocked(groupNotifications.get(i));
3464 // if there is a valid summary for this group, and we are snoozing the only
3465 // child, also snooze the summary
3466 if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
3467 if (groupNotifications.size() != 2) {
3468 snoozeNotificationLocked(r);
3470 // snooze summary and the one child
3471 for (int i = 0; i < groupNotifications.size(); i++) {
3472 snoozeNotificationLocked(groupNotifications.get(i));
3476 snoozeNotificationLocked(r);
3480 // just snooze the one notification
3481 snoozeNotificationLocked(r);
3485 @GuardedBy("mNotificationLock")
3486 void snoozeNotificationLocked(NotificationRecord r) {
3487 MetricsLogger.action(r.getLogMaker()
3488 .setCategory(MetricsEvent.NOTIFICATION_SNOOZED)
3489 .setType(MetricsEvent.TYPE_CLOSE)
3490 .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
3491 mSnoozeCriterionId == null ? 0 : 1));
3492 boolean wasPosted = removeFromNotificationListsLocked(r);
3493 cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
3494 updateLightsLocked();
3495 if (mSnoozeCriterionId != null) {
3496 mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
3497 mSnoozeHelper.snooze(r);
3499 mSnoozeHelper.snooze(r, mDuration);
3505 protected class EnqueueNotificationRunnable implements Runnable {
3506 private final NotificationRecord r;
3507 private final int userId;
3509 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3510 this.userId = userId;
3516 synchronized (mNotificationLock) {
3517 mEnqueuedNotifications.add(r);
3518 scheduleTimeoutLocked(r);
3520 final StatusBarNotification n = r.sbn;
3521 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3522 NotificationRecord old = mNotificationsByKey.get(n.getKey());
3524 // Retain ranking information from previous record
3525 r.copyRankingInformation(old);
3528 final int callingUid = n.getUid();
3529 final int callingPid = n.getInitialPid();
3530 final Notification notification = n.getNotification();
3531 final String pkg = n.getPackageName();
3532 final int id = n.getId();
3533 final String tag = n.getTag();
3535 // Handle grouped notifications and bail out early if we
3536 // can to avoid extracting signals.
3537 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3539 // if this is a group child, unsnooze parent summary
3540 if (n.isGroup() && notification.isGroupChild()) {
3541 mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3544 // This conditional is a dirty hack to limit the logging done on
3545 // behalf of the download manager without affecting other apps.
3546 if (!pkg.equals("com.android.providers.downloads")
3547 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3548 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3550 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3552 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3553 pkg, id, tag, userId, notification.toString(),
3557 mRankingHelper.extractSignals(r);
3559 // tell the assistant service about the notification
3560 if (mNotificationAssistants.isEnabled()) {
3561 mNotificationAssistants.onNotificationEnqueued(r);
3562 mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3563 DELAY_FOR_ASSISTANT_TIME);
3565 mHandler.post(new PostNotificationRunnable(r.getKey()));
3571 protected class PostNotificationRunnable implements Runnable {
3572 private final String key;
3574 PostNotificationRunnable(String key) {
3580 synchronized (mNotificationLock) {
3582 NotificationRecord r = null;
3583 int N = mEnqueuedNotifications.size();
3584 for (int i = 0; i < N; i++) {
3585 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3586 if (Objects.equals(key, enqueued.getKey())) {
3592 Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3595 NotificationRecord old = mNotificationsByKey.get(key);
3596 final StatusBarNotification n = r.sbn;
3597 final Notification notification = n.getNotification();
3598 int index = indexOfNotificationLocked(n.getKey());
3600 mNotificationList.add(r);
3601 mUsageStats.registerPostedByApp(r);
3603 old = mNotificationList.get(index);
3604 mNotificationList.set(index, r);
3605 mUsageStats.registerUpdatedByApp(r, old);
3606 // Make sure we don't lose the foreground service state.
3607 notification.flags |=
3608 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3612 mNotificationsByKey.put(n.getKey(), r);
3614 // Ensure if this is a foreground service that the proper additional
3616 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3617 notification.flags |= Notification.FLAG_ONGOING_EVENT
3618 | Notification.FLAG_NO_CLEAR;
3621 applyZenModeLocked(r);
3622 mRankingHelper.sort(mNotificationList);
3624 if (notification.getSmallIcon() != null) {
3625 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3626 mListeners.notifyPostedLocked(n, oldSbn);
3627 mHandler.post(new Runnable() {
3630 mGroupHelper.onNotificationPosted(n);
3634 Slog.e(TAG, "Not posting notification without small icon: " + notification);
3635 if (old != null && !old.isCanceled) {
3636 mListeners.notifyRemovedLocked(n,
3637 NotificationListenerService.REASON_ERROR);
3638 mHandler.post(new Runnable() {
3641 mGroupHelper.onNotificationRemoved(n);
3645 // ATTENTION: in a future release we will bail out here
3646 // so that we do not play sounds, show lights, etc. for invalid
3648 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3649 + n.getPackageName());
3652 buzzBeepBlinkLocked(r);
3654 int N = mEnqueuedNotifications.size();
3655 for (int i = 0; i < N; i++) {
3656 final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3657 if (Objects.equals(key, enqueued.getKey())) {
3658 mEnqueuedNotifications.remove(i);
3668 * Ensures that grouped notification receive their special treatment.
3670 * <p>Cancels group children if the new notification causes a group to lose
3673 * <p>Updates mSummaryByGroupKey.</p>
3675 @GuardedBy("mNotificationLock")
3676 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
3677 int callingUid, int callingPid) {
3678 StatusBarNotification sbn = r.sbn;
3679 Notification n = sbn.getNotification();
3680 if (n.isGroupSummary() && !sbn.isAppGroup()) {
3681 // notifications without a group shouldn't be a summary, otherwise autobundling can
3683 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
3686 String group = sbn.getGroupKey();
3687 boolean isSummary = n.isGroupSummary();
3689 Notification oldN = old != null ? old.sbn.getNotification() : null;
3690 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
3691 boolean oldIsSummary = old != null && oldN.isGroupSummary();
3694 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
3695 if (removedSummary != old) {
3697 removedSummary != null ? removedSummary.getKey() : "<null>";
3698 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
3699 ", removed=" + removedKey);
3703 mSummaryByGroupKey.put(group, r);
3706 // Clear out group children of the old notification if the update
3707 // causes the group summary to go away. This happens when the old
3708 // notification was a summary and the new one isn't, or when the old
3709 // notification was a summary and its group key changed.
3710 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
3711 cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
3716 @GuardedBy("mNotificationLock")
3717 void scheduleTimeoutLocked(NotificationRecord record) {
3718 if (record.getNotification().getTimeoutAfter() > 0) {
3719 final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
3720 REQUEST_CODE_TIMEOUT,
3721 new Intent(ACTION_NOTIFICATION_TIMEOUT)
3722 .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
3723 .appendPath(record.getKey()).build())
3724 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
3725 .putExtra(EXTRA_KEY, record.getKey()),
3726 PendingIntent.FLAG_UPDATE_CURRENT);
3727 mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
3728 SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
3733 @GuardedBy("mNotificationLock")
3734 void buzzBeepBlinkLocked(NotificationRecord record) {
3735 boolean buzz = false;
3736 boolean beep = false;
3737 boolean blink = false;
3739 final Notification notification = record.sbn.getNotification();
3740 final String key = record.getKey();
3742 // Should this notification make noise, vibe, or use the LED?
3743 final boolean aboveThreshold =
3744 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
3746 // Remember if this notification already owns the notification channels.
3747 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
3748 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
3749 // These are set inside the conditional if the notification is allowed to make noise.
3750 boolean hasValidVibrate = false;
3751 boolean hasValidSound = false;
3753 if (isNotificationForCurrentUser(record)) {
3754 // If the notification icon will appear in the status bar, AND it hasn't been blocked
3755 // by do-not-disturb, it should generate an accessibility event
3756 if (!record.isUpdate
3757 && !record.isIntercepted()
3758 && record.getImportance() > IMPORTANCE_MIN) {
3759 sendAccessibilityEvent(notification, record.sbn.getPackageName());
3761 if (aboveThreshold && mSystemReady && mAudioManager != null) {
3762 // this notification wants to make noise & is allowed to make noise
3763 Uri soundUri = record.getSound();
3764 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
3766 long[] vibration = record.getVibration();
3767 // Demote sound to vibration if vibration missing & phone in vibration mode.
3768 if (vibration == null
3770 && (mAudioManager.getRingerModeInternal()
3771 == AudioManager.RINGER_MODE_VIBRATE)) {
3772 vibration = mFallbackVibrationPattern;
3774 hasValidVibrate = vibration != null;
3776 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
3778 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
3779 if (DBG) Slog.v(TAG, "Interrupting!");
3780 if (hasValidSound) {
3781 mSoundNotificationKey = key;
3783 playInCallNotification();
3786 beep = playSound(record, soundUri);
3790 final boolean ringerModeSilent =
3791 mAudioManager.getRingerModeInternal()
3792 == AudioManager.RINGER_MODE_SILENT;
3793 if (!mInCall && hasValidVibrate && !ringerModeSilent) {
3794 mVibrateNotificationKey = key;
3796 buzz = playVibration(record, vibration, hasValidSound);
3801 // If a notification is updated to remove the actively playing sound or vibrate,
3802 // cancel that feedback now
3803 if (wasBeep && !hasValidSound) {
3806 if (wasBuzz && !hasValidVibrate) {
3807 clearVibrateLocked();
3811 // release the light
3812 boolean wasShowLights = mLights.remove(key);
3813 if (record.getLight() != null && aboveThreshold
3814 && ((record.getSuppressedVisualEffects()
3815 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
3817 updateLightsLocked();
3818 if (mUseAttentionLight) {
3819 mAttentionLight.pulse();
3822 } else if (wasShowLights) {
3823 updateLightsLocked();
3825 if (buzz || beep || blink) {
3826 MetricsLogger.action(record.getLogMaker()
3827 .setCategory(MetricsEvent.NOTIFICATION_ALERT)
3828 .setType(MetricsEvent.TYPE_OPEN)
3829 .setSubtype((buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)));
3830 EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
3834 @GuardedBy("mNotificationLock")
3835 boolean shouldMuteNotificationLocked(final NotificationRecord record) {
3836 // Suppressed because it's a silent update
3837 final Notification notification = record.getNotification();
3839 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
3843 // muted by listener
3844 final String disableEffects = disableNotificationEffects(record);
3845 if (disableEffects != null) {
3846 ZenLog.traceDisableEffects(record, disableEffects);
3850 // suppressed due to DND
3851 if (record.isIntercepted()) {
3855 // Suppressed because another notification in its group handles alerting
3856 if (record.sbn.isGroup()) {
3857 return notification.suppressAlertingDueToGrouping();
3860 // Suppressed for being too recently noisy
3861 final String pkg = record.sbn.getPackageName();
3862 if (mUsageStats.isAlertRateLimited(pkg)) {
3863 Slog.e(TAG, "Muting recently noisy " + record.getKey());
3870 private boolean playSound(final NotificationRecord record, Uri soundUri) {
3871 boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3872 // do not play notifications if there is a user of exclusive audio focus
3873 // or the device is in vibrate mode
3874 if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
3875 != AudioManager.RINGER_MODE_VIBRATE) {
3876 final long identity = Binder.clearCallingIdentity();
3878 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3879 if (player != null) {
3880 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3881 + " with attributes " + record.getAudioAttributes());
3882 player.playAsync(soundUri, record.sbn.getUser(), looping,
3883 record.getAudioAttributes());
3886 } catch (RemoteException e) {
3888 Binder.restoreCallingIdentity(identity);
3894 private boolean playVibration(final NotificationRecord record, long[] vibration,
3895 boolean delayVibForSound) {
3896 // Escalate privileges so we can use the vibrator even if the
3897 // notifying app does not have the VIBRATE permission.
3898 long identity = Binder.clearCallingIdentity();
3900 final VibrationEffect effect;
3902 final boolean insistent =
3903 (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
3904 effect = VibrationEffect.createWaveform(
3905 vibration, insistent ? 0 : -1 /*repeatIndex*/);
3906 } catch (IllegalArgumentException e) {
3907 Slog.e(TAG, "Error creating vibration waveform with pattern: " +
3908 Arrays.toString(vibration));
3911 if (delayVibForSound) {
3913 // delay the vibration by the same amount as the notification sound
3914 final int waitMs = mAudioManager.getFocusRampTimeMs(
3915 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
3916 record.getAudioAttributes());
3917 if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
3919 Thread.sleep(waitMs);
3920 } catch (InterruptedException e) { }
3921 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3922 effect, record.getAudioAttributes());
3925 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3926 effect, record.getAudioAttributes());
3930 Binder.restoreCallingIdentity(identity);
3934 private boolean isNotificationForCurrentUser(NotificationRecord record) {
3935 final int currentUser;
3936 final long token = Binder.clearCallingIdentity();
3938 currentUser = ActivityManager.getCurrentUser();
3940 Binder.restoreCallingIdentity(token);
3942 return (record.getUserId() == UserHandle.USER_ALL ||
3943 record.getUserId() == currentUser ||
3944 mUserProfiles.isCurrentProfile(record.getUserId()));
3947 private void playInCallNotification() {
3951 // If toneGenerator creation fails, just continue the call
3952 // without playing the notification sound.
3954 synchronized (mInCallToneGeneratorLock) {
3955 if (mInCallToneGenerator != null) {
3956 // limit this tone to 1 second; BEEP2 should in fact be much shorter
3957 mInCallToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2, 1000);
3960 } catch (RuntimeException e) {
3961 Log.w(TAG, "Exception from ToneGenerator: " + e);
3967 @GuardedBy("mToastQueue")
3968 void showNextToastLocked() {
3969 ToastRecord record = mToastQueue.get(0);
3970 while (record != null) {
3971 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3973 record.callback.show(record.token);
3974 scheduleTimeoutLocked(record);
3976 } catch (RemoteException e) {
3977 Slog.w(TAG, "Object died trying to show notification " + record.callback
3978 + " in package " + record.pkg);
3979 // remove it from the list and let the process die
3980 int index = mToastQueue.indexOf(record);
3982 mToastQueue.remove(index);
3984 keepProcessAliveIfNeededLocked(record.pid);
3985 if (mToastQueue.size() > 0) {
3986 record = mToastQueue.get(0);
3994 @GuardedBy("mToastQueue")
3995 void cancelToastLocked(int index) {
3996 ToastRecord record = mToastQueue.get(index);
3998 record.callback.hide();
3999 } catch (RemoteException e) {
4000 Slog.w(TAG, "Object died trying to hide notification " + record.callback
4001 + " in package " + record.pkg);
4002 // don't worry about this, we're about to remove it from
4006 ToastRecord lastToast = mToastQueue.remove(index);
4007 mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY);
4009 keepProcessAliveIfNeededLocked(record.pid);
4010 if (mToastQueue.size() > 0) {
4011 // Show the next one. If the callback fails, this will remove
4012 // it from the list, so don't assume that the list hasn't changed
4013 // after this point.
4014 showNextToastLocked();
4018 @GuardedBy("mToastQueue")
4019 private void scheduleTimeoutLocked(ToastRecord r)
4021 mHandler.removeCallbacksAndMessages(r);
4022 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
4023 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
4024 mHandler.sendMessageDelayed(m, delay);
4027 private void handleTimeout(ToastRecord record)
4029 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
4030 synchronized (mToastQueue) {
4031 int index = indexOfToastLocked(record.pkg, record.callback);
4033 cancelToastLocked(index);
4038 @GuardedBy("mToastQueue")
4039 int indexOfToastLocked(String pkg, ITransientNotification callback)
4041 IBinder cbak = callback.asBinder();
4042 ArrayList<ToastRecord> list = mToastQueue;
4043 int len = list.size();
4044 for (int i=0; i<len; i++) {
4045 ToastRecord r = list.get(i);
4046 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
4053 @GuardedBy("mToastQueue")
4054 void keepProcessAliveIfNeededLocked(int pid)
4056 int toastCount = 0; // toasts from this pid
4057 ArrayList<ToastRecord> list = mToastQueue;
4058 int N = list.size();
4059 for (int i=0; i<N; i++) {
4060 ToastRecord r = list.get(i);
4066 mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
4067 } catch (RemoteException e) {
4068 // Shouldn't happen.
4072 private void handleRankingReconsideration(Message message) {
4073 if (!(message.obj instanceof RankingReconsideration)) return;
4074 RankingReconsideration recon = (RankingReconsideration) message.obj;
4077 synchronized (mNotificationLock) {
4078 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
4079 if (record == null) {
4082 int indexBefore = findNotificationRecordIndexLocked(record);
4083 boolean interceptBefore = record.isIntercepted();
4084 float contactAffinityBefore = record.getContactAffinity();
4085 int visibilityBefore = record.getPackageVisibilityOverride();
4086 recon.applyChangesLocked(record);
4087 applyZenModeLocked(record);
4088 mRankingHelper.sort(mNotificationList);
4089 int indexAfter = findNotificationRecordIndexLocked(record);
4090 boolean interceptAfter = record.isIntercepted();
4091 float contactAffinityAfter = record.getContactAffinity();
4092 int visibilityAfter = record.getPackageVisibilityOverride();
4093 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
4094 || visibilityBefore != visibilityAfter;
4095 if (interceptBefore && !interceptAfter
4096 && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
4097 buzzBeepBlinkLocked(record);
4101 scheduleSendRankingUpdate();
4105 private void handleRankingSort(Message msg) {
4106 if (!(msg.obj instanceof Boolean)) return;
4107 if (mRankingHelper == null) return;
4108 boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
4109 synchronized (mNotificationLock) {
4110 final int N = mNotificationList.size();
4111 // Any field that can change via one of the extractors or by the assistant
4112 // needs to be added here.
4113 ArrayList<String> orderBefore = new ArrayList<String>(N);
4114 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
4115 int[] visibilities = new int[N];
4116 boolean[] showBadges = new boolean[N];
4117 for (int i = 0; i < N; i++) {
4118 final NotificationRecord r = mNotificationList.get(i);
4119 orderBefore.add(r.getKey());
4120 groupOverrideBefore.add(r.sbn.getGroupKey());
4121 visibilities[i] = r.getPackageVisibilityOverride();
4122 showBadges[i] = r.canShowBadge();
4123 mRankingHelper.extractSignals(r);
4125 mRankingHelper.sort(mNotificationList);
4126 for (int i = 0; i < N; i++) {
4127 final NotificationRecord r = mNotificationList.get(i);
4129 || !orderBefore.get(i).equals(r.getKey())
4130 || visibilities[i] != r.getPackageVisibilityOverride()
4131 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
4132 || showBadges[i] != r.canShowBadge()) {
4133 scheduleSendRankingUpdate();
4140 @GuardedBy("mNotificationLock")
4141 private void recordCallerLocked(NotificationRecord record) {
4142 if (mZenModeHelper.isCall(record)) {
4143 mZenModeHelper.recordCaller(record);
4147 // let zen mode evaluate this record
4148 @GuardedBy("mNotificationLock")
4149 private void applyZenModeLocked(NotificationRecord record) {
4150 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
4151 if (record.isIntercepted()) {
4152 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
4153 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
4154 | (mZenModeHelper.shouldSuppressWhenScreenOn()
4155 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
4156 record.setSuppressedVisualEffects(suppressed);
4158 record.setSuppressedVisualEffects(0);
4162 @GuardedBy("mNotificationLock")
4163 private int findNotificationRecordIndexLocked(NotificationRecord target) {
4164 return mRankingHelper.indexOf(mNotificationList, target);
4167 private void scheduleSendRankingUpdate() {
4168 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
4169 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
4170 mHandler.sendMessage(m);
4174 private void handleSendRankingUpdate() {
4175 synchronized (mNotificationLock) {
4176 mListeners.notifyRankingUpdateLocked();
4180 private void scheduleListenerHintsChanged(int state) {
4181 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
4182 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
4185 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
4186 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
4187 mHandler.obtainMessage(
4188 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
4189 listenerInterruptionFilter,
4193 private void handleListenerHintsChanged(int hints) {
4194 synchronized (mNotificationLock) {
4195 mListeners.notifyListenerHintsChangedLocked(hints);
4199 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
4200 synchronized (mNotificationLock) {
4201 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
4205 private final class WorkerHandler extends Handler
4207 public WorkerHandler(Looper looper) {
4212 public void handleMessage(Message msg)
4216 case MESSAGE_TIMEOUT:
4217 handleTimeout((ToastRecord)msg.obj);
4219 case MESSAGE_SAVE_POLICY_FILE:
4220 handleSavePolicyFile();
4222 case MESSAGE_SEND_RANKING_UPDATE:
4223 handleSendRankingUpdate();
4225 case MESSAGE_LISTENER_HINTS_CHANGED:
4226 handleListenerHintsChanged(msg.arg1);
4228 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
4229 handleListenerInterruptionFilterChanged(msg.arg1);
4236 private final class RankingHandlerWorker extends Handler implements RankingHandler
4238 public RankingHandlerWorker(Looper looper) {
4243 public void handleMessage(Message msg) {
4245 case MESSAGE_RECONSIDER_RANKING:
4246 handleRankingReconsideration(msg);
4248 case MESSAGE_RANKING_SORT:
4249 handleRankingSort(msg);
4254 public void requestSort(boolean forceUpdate) {
4255 removeMessages(MESSAGE_RANKING_SORT);
4256 Message msg = Message.obtain();
4257 msg.what = MESSAGE_RANKING_SORT;
4258 msg.obj = forceUpdate;
4262 public void requestReconsideration(RankingReconsideration recon) {
4263 Message m = Message.obtain(this,
4264 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
4265 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
4266 sendMessageDelayed(m, delay);
4271 // ============================================================================
4272 static int clamp(int x, int low, int high) {
4273 return (x < low) ? low : ((x > high) ? high : x);
4277 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
4278 final AccessibilityManager accessibilityManager
4279 = AccessibilityManager.getInstance(getContext());
4280 if (accessibilityManager == null || !accessibilityManager.isEnabled()) {
4284 AccessibilityEvent event =
4285 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
4286 event.setPackageName(packageName);
4287 event.setClassName(Notification.class.getName());
4288 event.setParcelableData(notification);
4289 CharSequence tickerText = notification.tickerText;
4290 if (!TextUtils.isEmpty(tickerText)) {
4291 event.getText().add(tickerText);
4294 accessibilityManager.sendAccessibilityEvent(event);
4298 * Removes all NotificationsRecords with the same key as the given notification record
4299 * from both lists. Do not call this method while iterating over either list.
4301 @GuardedBy("mNotificationLock")
4302 private boolean removeFromNotificationListsLocked(NotificationRecord r) {
4303 // Remove from both lists, either list could have a separate Record for what is
4304 // effectively the same notification.
4305 boolean wasPosted = false;
4306 NotificationRecord recordInList = null;
4307 if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
4309 mNotificationList.remove(recordInList);
4310 mNotificationsByKey.remove(recordInList.sbn.getKey());
4313 while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
4315 mEnqueuedNotifications.remove(recordInList);
4320 @GuardedBy("mNotificationLock")
4321 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
4322 boolean wasPosted) {
4323 final String canceledKey = r.getKey();
4326 recordCallerLocked(r);
4330 if (r.getNotification().deleteIntent != null) {
4332 r.getNotification().deleteIntent.send();
4333 } catch (PendingIntent.CanceledException ex) {
4334 // do nothing - there's no relevant way to recover, and
4335 // no reason to let this propagate
4336 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
4341 // Only cancel these if this notification actually got to be posted.
4344 if (r.getNotification().getSmallIcon() != null) {
4345 if (reason != REASON_SNOOZED) {
4346 r.isCanceled = true;
4348 mListeners.notifyRemovedLocked(r.sbn, reason);
4349 mHandler.post(new Runnable() {
4352 mGroupHelper.onNotificationRemoved(r.sbn);
4358 if (canceledKey.equals(mSoundNotificationKey)) {
4359 mSoundNotificationKey = null;
4360 final long identity = Binder.clearCallingIdentity();
4362 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
4363 if (player != null) {
4366 } catch (RemoteException e) {
4368 Binder.restoreCallingIdentity(identity);
4373 if (canceledKey.equals(mVibrateNotificationKey)) {
4374 mVibrateNotificationKey = null;
4375 long identity = Binder.clearCallingIdentity();
4380 Binder.restoreCallingIdentity(identity);
4385 mLights.remove(canceledKey);
4388 // Record usage stats
4389 // TODO: add unbundling stats?
4392 case REASON_CANCEL_ALL:
4393 case REASON_LISTENER_CANCEL:
4394 case REASON_LISTENER_CANCEL_ALL:
4395 mUsageStats.registerDismissedByUser(r);
4397 case REASON_APP_CANCEL:
4398 case REASON_APP_CANCEL_ALL:
4399 mUsageStats.registerRemovedByApp(r);
4403 String groupKey = r.getGroupKey();
4404 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
4405 if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
4406 mSummaryByGroupKey.remove(groupKey);
4408 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
4409 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
4410 summaries.remove(r.sbn.getPackageName());
4413 // Save it for users of getHistoricalNotifications()
4414 mArchive.record(r.sbn);
4416 final long now = System.currentTimeMillis();
4417 MetricsLogger.action(r.getLogMaker(now)
4418 .setCategory(MetricsEvent.NOTIFICATION_ITEM)
4419 .setType(MetricsEvent.TYPE_DISMISS)
4420 .setSubtype(reason));
4421 EventLogTags.writeNotificationCanceled(canceledKey, reason,
4422 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
4426 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
4427 * and none of the {@code mustNotHaveFlags}.
4429 void cancelNotification(final int callingUid, final int callingPid,
4430 final String pkg, final String tag, final int id,
4431 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
4432 final int userId, final int reason, final ManagedServiceInfo listener) {
4433 // In enqueueNotificationInternal notifications are added by scheduling the
4434 // work on the worker handler. Hence, we also schedule the cancel on this
4435 // handler to avoid a scenario where an add notification call followed by a
4436 // remove notification call ends up in not removing the notification.
4437 mHandler.post(new Runnable() {
4440 String listenerName = listener == null ? null : listener.component.toShortString();
4441 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
4442 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
4444 synchronized (mNotificationLock) {
4445 // Look for the notification, searching both the posted and enqueued lists.
4446 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
4448 // The notification was found, check if it should be removed.
4450 // Ideally we'd do this in the caller of this method. However, that would
4451 // require the caller to also find the notification.
4452 if (reason == REASON_CLICK) {
4453 mUsageStats.registerClickedByUser(r);
4456 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
4459 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
4463 // Cancel the notification.
4464 boolean wasPosted = removeFromNotificationListsLocked(r);
4465 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
4466 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
4468 updateLightsLocked();
4470 // No notification was found, assume that it is snoozed and cancel it.
4471 if (reason != REASON_SNOOZED) {
4472 final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
4484 * Determine whether the userId applies to the notification in question, either because
4485 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
4487 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
4489 // looking for USER_ALL notifications? match everything
4490 userId == UserHandle.USER_ALL
4491 // a notification sent to USER_ALL matches any query
4492 || r.getUserId() == UserHandle.USER_ALL
4493 // an exact user match
4494 || r.getUserId() == userId;
4498 * Determine whether the userId applies to the notification in question, either because
4499 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
4500 * because it matches one of the users profiles.
4502 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
4503 return notificationMatchesUserId(r, userId)
4504 || mUserProfiles.isCurrentProfile(r.getUserId());
4508 * Cancels all notifications from a given package that have all of the
4509 * {@code mustHaveFlags}.
4511 void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
4512 int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
4513 ManagedServiceInfo listener) {
4514 mHandler.post(new Runnable() {
4517 String listenerName = listener == null ? null : listener.component.toShortString();
4518 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4519 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
4522 // Why does this parameter exist? Do we actually want to execute the above if doit
4528 synchronized (mNotificationLock) {
4529 FlagChecker flagChecker = (int flags) -> {
4530 if ((flags & mustHaveFlags) != mustHaveFlags) {
4533 if ((flags & mustNotHaveFlags) != 0) {
4539 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4540 pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
4541 false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
4542 listenerName, true /* wasPosted */);
4543 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4544 callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
4545 flagChecker, false /*includeCurrentProfiles*/, userId,
4546 false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
4547 mSnoozeHelper.cancel(userId, pkg);
4553 private interface FlagChecker {
4554 // Returns false if these flags do not pass the defined flag test.
4555 public boolean apply(int flags);
4558 @GuardedBy("mNotificationLock")
4559 private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
4560 int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
4561 String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
4562 boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
4563 ArrayList<NotificationRecord> canceledNotifications = null;
4564 for (int i = notificationList.size() - 1; i >= 0; --i) {
4565 NotificationRecord r = notificationList.get(i);
4566 if (includeCurrentProfiles) {
4567 if (!notificationMatchesCurrentProfiles(r, userId)) {
4570 } else if (!notificationMatchesUserId(r, userId)) {
4573 // Don't remove notifications to all, if there's no package name specified
4574 if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
4577 if (!flagChecker.apply(r.getFlags())) {
4580 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
4583 if (channelId != null && !channelId.equals(r.getChannel().getId())) {
4587 if (canceledNotifications == null) {
4588 canceledNotifications = new ArrayList<>();
4590 notificationList.remove(i);
4591 mNotificationsByKey.remove(r.getKey());
4592 canceledNotifications.add(r);
4593 cancelNotificationLocked(r, sendDelete, reason, wasPosted);
4595 if (canceledNotifications != null) {
4596 final int M = canceledNotifications.size();
4597 for (int i = 0; i < M; i++) {
4598 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
4599 listenerName, false /* sendDelete */);
4601 updateLightsLocked();
4605 void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
4606 ManagedServiceInfo listener) {
4607 String listenerName = listener == null ? null : listener.component.toShortString();
4608 if (duration <= 0 && snoozeCriterionId == null || key == null) {
4613 Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
4614 snoozeCriterionId, listenerName));
4616 // Needs to post so that it can cancel notifications not yet enqueued.
4617 mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
4620 void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
4621 String listenerName = listener == null ? null : listener.component.toShortString();
4623 Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
4625 mSnoozeHelper.repost(key);
4629 @GuardedBy("mNotificationLock")
4630 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
4631 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
4632 mHandler.post(new Runnable() {
4635 synchronized (mNotificationLock) {
4636 String listenerName =
4637 listener == null ? null : listener.component.toShortString();
4638 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
4639 null, userId, 0, 0, reason, listenerName);
4641 FlagChecker flagChecker = (int flags) -> {
4642 if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
4649 cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
4650 null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
4651 includeCurrentProfiles, userId, true /*sendDelete*/, reason,
4652 listenerName, true);
4653 cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
4654 callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
4655 flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
4656 reason, listenerName, false);
4657 mSnoozeHelper.cancel(userId, includeCurrentProfiles);
4663 // Warning: The caller is responsible for invoking updateLightsLocked().
4664 @GuardedBy("mNotificationLock")
4665 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
4666 String listenerName, boolean sendDelete) {
4667 Notification n = r.getNotification();
4668 if (!n.isGroupSummary()) {
4672 String pkg = r.sbn.getPackageName();
4675 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
4679 cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
4681 cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
4682 listenerName, sendDelete, false);
4685 @GuardedBy("mNotificationLock")
4686 private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
4687 NotificationRecord parentNotification, int callingUid, int callingPid,
4688 String listenerName, boolean sendDelete, boolean wasPosted) {
4689 final String pkg = parentNotification.sbn.getPackageName();
4690 final int userId = parentNotification.getUserId();
4691 final int reason = REASON_GROUP_SUMMARY_CANCELED;
4692 for (int i = notificationList.size() - 1; i >= 0; i--) {
4693 final NotificationRecord childR = notificationList.get(i);
4694 final StatusBarNotification childSbn = childR.sbn;
4695 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
4696 childR.getGroupKey().equals(parentNotification.getGroupKey())
4697 && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
4698 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
4699 childSbn.getTag(), userId, 0, 0, reason, listenerName);
4700 notificationList.remove(i);
4701 mNotificationsByKey.remove(childR.getKey());
4702 cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
4707 @GuardedBy("mNotificationLock")
4708 void updateLightsLocked()
4710 // handle notification lights
4711 NotificationRecord ledNotification = null;
4712 while (ledNotification == null && !mLights.isEmpty()) {
4713 final String owner = mLights.get(mLights.size() - 1);
4714 ledNotification = mNotificationsByKey.get(owner);
4715 if (ledNotification == null) {
4716 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
4717 mLights.remove(owner);
4721 // Don't flash while we are in a call or screen is on
4722 if (ledNotification == null || mInCall || mScreenOn) {
4723 mNotificationLight.turnOff();
4725 NotificationRecord.Light light = ledNotification.getLight();
4726 if (light != null && mNotificationPulseEnabled) {
4728 mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
4729 light.onMs, light.offMs);
4734 @GuardedBy("mNotificationLock")
4735 @NonNull List<NotificationRecord> findGroupNotificationsLocked(String pkg,
4736 String groupKey, int userId) {
4737 List<NotificationRecord> records = new ArrayList<>();
4738 records.addAll(findGroupNotificationByListLocked(mNotificationList, pkg, groupKey, userId));
4740 findGroupNotificationByListLocked(mEnqueuedNotifications, pkg, groupKey, userId));
4745 @GuardedBy("mNotificationLock")
4746 private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
4747 ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
4748 List<NotificationRecord> records = new ArrayList<>();
4749 final int len = list.size();
4750 for (int i = 0; i < len; i++) {
4751 NotificationRecord r = list.get(i);
4752 if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
4753 && r.sbn.getPackageName().equals(pkg)) {
4760 // Searches both enqueued and posted notifications by key.
4761 // TODO: need to combine a bunch of these getters with slightly different behavior.
4762 // TODO: Should enqueuing just add to mNotificationsByKey instead?
4763 @GuardedBy("mNotificationLock")
4764 private NotificationRecord findNotificationByKeyLocked(String key) {
4765 NotificationRecord r;
4766 if ((r = findNotificationByListLocked(mNotificationList, key)) != null) {
4769 if ((r = findNotificationByListLocked(mEnqueuedNotifications, key)) != null) {
4775 @GuardedBy("mNotificationLock")
4776 NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
4777 NotificationRecord r;
4778 if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
4781 if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
4788 @GuardedBy("mNotificationLock")
4789 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4790 String pkg, String tag, int id, int userId) {
4791 final int len = list.size();
4792 for (int i = 0; i < len; i++) {
4793 NotificationRecord r = list.get(i);
4794 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
4795 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
4802 @GuardedBy("mNotificationLock")
4803 private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
4805 final int N = list.size();
4806 for (int i = 0; i < N; i++) {
4807 if (key.equals(list.get(i).getKey())) {
4814 @GuardedBy("mNotificationLock")
4815 int indexOfNotificationLocked(String key) {
4816 final int N = mNotificationList.size();
4817 for (int i = 0; i < N; i++) {
4818 if (key.equals(mNotificationList.get(i).getKey())) {
4825 private void updateNotificationPulse() {
4826 synchronized (mNotificationLock) {
4827 updateLightsLocked();
4831 protected boolean isCallingUidSystem() {
4832 final int uid = Binder.getCallingUid();
4833 return uid == Process.SYSTEM_UID;
4836 protected boolean isUidSystemOrPhone(int uid) {
4837 final int appid = UserHandle.getAppId(uid);
4838 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
4841 // TODO: Most calls should probably move to isCallerSystem.
4842 protected boolean isCallerSystemOrPhone() {
4843 return isUidSystemOrPhone(Binder.getCallingUid());
4846 private void checkCallerIsSystem() {
4847 if (isCallerSystemOrPhone()) {
4850 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
4853 private void checkCallerIsSystemOrSameApp(String pkg) {
4854 if (isCallerSystemOrPhone()) {
4857 checkCallerIsSameApp(pkg);
4860 private boolean isCallerInstantApp(String pkg) {
4861 // System is always allowed to act for ephemeral apps.
4862 if (isCallerSystemOrPhone()) {
4866 mAppOps.checkPackage(Binder.getCallingUid(), pkg);
4869 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
4870 UserHandle.getCallingUserId());
4872 throw new SecurityException("Unknown package " + pkg);
4874 return ai.isInstantApp();
4875 } catch (RemoteException re) {
4876 throw new SecurityException("Unknown package " + pkg, re);
4881 private void checkCallerIsSameApp(String pkg) {
4882 final int uid = Binder.getCallingUid();
4884 ApplicationInfo ai = mPackageManager.getApplicationInfo(
4885 pkg, 0, UserHandle.getCallingUserId());
4887 throw new SecurityException("Unknown package " + pkg);
4889 if (!UserHandle.isSameApp(ai.uid, uid)) {
4890 throw new SecurityException("Calling uid " + uid + " gave package "
4891 + pkg + " which is owned by uid " + ai.uid);
4893 } catch (RemoteException re) {
4894 throw new SecurityException("Unknown package " + pkg + "\n" + re);
4898 private static String callStateToString(int state) {
4900 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
4901 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
4902 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
4903 default: return "CALL_STATE_UNKNOWN_" + state;
4907 private void listenForCallState() {
4908 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
4910 public void onCallStateChanged(int state, String incomingNumber) {
4911 if (mCallState == state) return;
4912 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
4915 }, PhoneStateListener.LISTEN_CALL_STATE);
4919 * Generates a NotificationRankingUpdate from 'sbns', considering only
4920 * notifications visible to the given listener.
4922 @GuardedBy("mNotificationLock")
4923 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
4924 final int N = mNotificationList.size();
4925 ArrayList<String> keys = new ArrayList<String>(N);
4926 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
4927 ArrayList<Integer> importance = new ArrayList<>(N);
4928 Bundle overrideGroupKeys = new Bundle();
4929 Bundle visibilityOverrides = new Bundle();
4930 Bundle suppressedVisualEffects = new Bundle();
4931 Bundle explanation = new Bundle();
4932 Bundle channels = new Bundle();
4933 Bundle overridePeople = new Bundle();
4934 Bundle snoozeCriteria = new Bundle();
4935 Bundle showBadge = new Bundle();
4936 for (int i = 0; i < N; i++) {
4937 NotificationRecord record = mNotificationList.get(i);
4938 if (!isVisibleToListener(record.sbn, info)) {
4941 final String key = record.sbn.getKey();
4943 importance.add(record.getImportance());
4944 if (record.getImportanceExplanation() != null) {
4945 explanation.putCharSequence(key, record.getImportanceExplanation());
4947 if (record.isIntercepted()) {
4948 interceptedKeys.add(key);
4951 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
4952 if (record.getPackageVisibilityOverride()
4953 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
4954 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
4956 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
4957 channels.putParcelable(key, record.getChannel());
4958 overridePeople.putStringArrayList(key, record.getPeopleOverride());
4959 snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
4960 showBadge.putBoolean(key, record.canShowBadge());
4962 final int M = keys.size();
4963 String[] keysAr = keys.toArray(new String[M]);
4964 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
4965 int[] importanceAr = new int[M];
4966 for (int i = 0; i < M; i++) {
4967 importanceAr[i] = importance.get(i);
4969 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
4970 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
4971 channels, overridePeople, snoozeCriteria, showBadge);
4974 boolean hasCompanionDevice(ManagedServiceInfo info) {
4975 if (mCompanionManager == null) {
4976 mCompanionManager = getCompanionManager();
4978 // Companion mgr doesn't exist on all device types
4979 if (mCompanionManager == null) {
4982 long identity = Binder.clearCallingIdentity();
4984 List<String> associations = mCompanionManager.getAssociations(
4985 info.component.getPackageName(), info.userid);
4986 if (!ArrayUtils.isEmpty(associations)) {
4989 } catch (SecurityException se) {
4990 // Not a privileged listener
4991 } catch (RemoteException re) {
4992 Slog.e(TAG, "Cannot reach companion device service", re);
4993 } catch (Exception e) {
4994 Slog.e(TAG, "Cannot verify listener " + info, e);
4996 Binder.restoreCallingIdentity(identity);
5001 protected ICompanionDeviceManager getCompanionManager() {
5002 return ICompanionDeviceManager.Stub.asInterface(
5003 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
5006 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
5007 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
5010 // TODO: remove this for older listeners.
5014 private boolean isPackageSuspendedForUser(String pkg, int uid) {
5015 int userId = UserHandle.getUserId(uid);
5017 return mPackageManager.isPackageSuspendedForUser(pkg, userId);
5018 } catch (RemoteException re) {
5019 throw new SecurityException("Could not talk to package manager service");
5020 } catch (IllegalArgumentException ex) {
5021 // Package not found.
5026 private class TrimCache {
5027 StatusBarNotification heavy;
5028 StatusBarNotification sbnClone;
5029 StatusBarNotification sbnCloneLight;
5031 TrimCache(StatusBarNotification sbn) {
5035 StatusBarNotification ForListener(ManagedServiceInfo info) {
5036 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
5037 if (sbnCloneLight == null) {
5038 sbnCloneLight = heavy.cloneLight();
5040 return sbnCloneLight;
5042 if (sbnClone == null) {
5043 sbnClone = heavy.clone();
5050 public class NotificationAssistants extends ManagedServices {
5052 public NotificationAssistants() {
5053 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
5057 protected Config getConfig() {
5058 Config c = new Config();
5059 c.caption = "notification assistant service";
5060 c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
5061 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
5062 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
5063 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
5064 c.clientLabel = R.string.notification_ranker_binding_label;
5069 protected IInterface asInterface(IBinder binder) {
5070 return INotificationListener.Stub.asInterface(binder);
5074 protected boolean checkType(IInterface service) {
5075 return service instanceof INotificationListener;
5079 protected void onServiceAdded(ManagedServiceInfo info) {
5080 mListeners.registerGuestService(info);
5084 @GuardedBy("mNotificationLock")
5085 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5086 mListeners.unregisterService(removed.service, removed.userid);
5089 public void onNotificationEnqueued(final NotificationRecord r) {
5090 final StatusBarNotification sbn = r.sbn;
5091 TrimCache trimCache = new TrimCache(sbn);
5093 // There should be only one, but it's a list, so while we enforce
5094 // singularity elsewhere, we keep it general here, to avoid surprises.
5095 for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
5096 boolean sbnVisible = isVisibleToListener(sbn, info);
5101 final int importance = r.getImportance();
5102 final boolean fromUser = r.isImportanceFromUser();
5103 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5104 mHandler.post(new Runnable() {
5107 notifyEnqueued(info, sbnToPost);
5113 private void notifyEnqueued(final ManagedServiceInfo info,
5114 final StatusBarNotification sbn) {
5115 final INotificationListener assistant = (INotificationListener) info.service;
5116 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5118 assistant.onNotificationEnqueued(sbnHolder);
5119 } catch (RemoteException ex) {
5120 Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
5125 * asynchronously notify the assistant that a notification has been snoozed until a
5128 @GuardedBy("mNotificationLock")
5129 public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
5130 final String snoozeCriterionId) {
5131 TrimCache trimCache = new TrimCache(sbn);
5132 for (final ManagedServiceInfo info : getServices()) {
5133 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5134 mHandler.post(new Runnable() {
5137 final INotificationListener assistant =
5138 (INotificationListener) info.service;
5139 StatusBarNotificationHolder sbnHolder
5140 = new StatusBarNotificationHolder(sbnToPost);
5142 assistant.onNotificationSnoozedUntilContext(
5143 sbnHolder, snoozeCriterionId);
5144 } catch (RemoteException ex) {
5145 Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
5152 public boolean isEnabled() {
5153 return !getServices().isEmpty();
5157 public class NotificationListeners extends ManagedServices {
5159 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
5161 public NotificationListeners() {
5162 super(getContext(), mHandler, mNotificationLock, mUserProfiles);
5166 protected Config getConfig() {
5167 Config c = new Config();
5168 c.caption = "notification listener";
5169 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
5170 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
5171 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
5172 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
5173 c.clientLabel = R.string.notification_listener_binding_label;
5178 protected IInterface asInterface(IBinder binder) {
5179 return INotificationListener.Stub.asInterface(binder);
5183 protected boolean checkType(IInterface service) {
5184 return service instanceof INotificationListener;
5188 public void onServiceAdded(ManagedServiceInfo info) {
5189 final INotificationListener listener = (INotificationListener) info.service;
5190 final NotificationRankingUpdate update;
5191 synchronized (mNotificationLock) {
5192 update = makeRankingUpdateLocked(info);
5195 listener.onListenerConnected(update);
5196 } catch (RemoteException e) {
5202 @GuardedBy("mNotificationLock")
5203 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
5204 if (removeDisabledHints(removed)) {
5205 updateListenerHintsLocked();
5206 updateEffectsSuppressorLocked();
5208 mLightTrimListeners.remove(removed);
5211 @GuardedBy("mNotificationLock")
5212 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
5213 if (trim == TRIM_LIGHT) {
5214 mLightTrimListeners.add(info);
5216 mLightTrimListeners.remove(info);
5220 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
5221 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
5225 * asynchronously notify all listeners about a new notification
5228 * Also takes care of removing a notification that has been visible to a listener before,
5229 * but isn't anymore.
5231 @GuardedBy("mNotificationLock")
5232 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5233 // Lazily initialized snapshots of the notification.
5234 TrimCache trimCache = new TrimCache(sbn);
5236 for (final ManagedServiceInfo info : getServices()) {
5237 boolean sbnVisible = isVisibleToListener(sbn, info);
5238 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5239 // This notification hasn't been and still isn't visible -> ignore.
5240 if (!oldSbnVisible && !sbnVisible) {
5243 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5245 // This notification became invisible -> remove the old one.
5246 if (oldSbnVisible && !sbnVisible) {
5247 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5248 mHandler.post(new Runnable() {
5251 notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5257 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
5258 mHandler.post(new Runnable() {
5261 notifyPosted(info, sbnToPost, update);
5268 * asynchronously notify all listeners about a removed notification
5270 @GuardedBy("mNotificationLock")
5271 public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
5272 // make a copy in case changes are made to the underlying Notification object
5273 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
5275 final StatusBarNotification sbnLight = sbn.cloneLight();
5276 for (final ManagedServiceInfo info : getServices()) {
5277 if (!isVisibleToListener(sbn, info)) {
5280 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5281 mHandler.post(new Runnable() {
5284 notifyRemoved(info, sbnLight, update, reason);
5291 * asynchronously notify all listeners about a reordering of notifications
5293 @GuardedBy("mNotificationLock")
5294 public void notifyRankingUpdateLocked() {
5295 for (final ManagedServiceInfo serviceInfo : getServices()) {
5296 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5299 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
5300 mHandler.post(new Runnable() {
5303 notifyRankingUpdate(serviceInfo, update);
5309 @GuardedBy("mNotificationLock")
5310 public void notifyListenerHintsChangedLocked(final int hints) {
5311 for (final ManagedServiceInfo serviceInfo : getServices()) {
5312 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5315 mHandler.post(new Runnable() {
5318 notifyListenerHintsChanged(serviceInfo, hints);
5324 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
5325 for (final ManagedServiceInfo serviceInfo : getServices()) {
5326 if (!serviceInfo.isEnabledForCurrentProfiles()) {
5329 mHandler.post(new Runnable() {
5332 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
5338 protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
5339 final NotificationChannel channel, final int modificationType) {
5340 if (channel == null) {
5343 for (final ManagedServiceInfo serviceInfo : getServices()) {
5344 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5348 mHandler.post(new Runnable() {
5351 if (hasCompanionDevice(serviceInfo)) {
5352 notifyNotificationChannelChanged(
5353 serviceInfo, pkg, user, channel, modificationType);
5360 protected void notifyNotificationChannelGroupChanged(
5361 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5362 final int modificationType) {
5363 if (group == null) {
5366 for (final ManagedServiceInfo serviceInfo : getServices()) {
5367 if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
5371 mHandler.post(new Runnable() {
5374 if (hasCompanionDevice(serviceInfo)) {
5375 notifyNotificationChannelGroupChanged(
5376 serviceInfo, pkg, user, group, modificationType);
5383 private void notifyPosted(final ManagedServiceInfo info,
5384 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5385 final INotificationListener listener = (INotificationListener) info.service;
5386 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5388 listener.onNotificationPosted(sbnHolder, rankingUpdate);
5389 } catch (RemoteException ex) {
5390 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5394 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
5395 NotificationRankingUpdate rankingUpdate, int reason) {
5396 if (!info.enabledAndUserMatches(sbn.getUserId())) {
5399 final INotificationListener listener = (INotificationListener) info.service;
5400 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5402 listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
5403 } catch (RemoteException ex) {
5404 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
5408 private void notifyRankingUpdate(ManagedServiceInfo info,
5409 NotificationRankingUpdate rankingUpdate) {
5410 final INotificationListener listener = (INotificationListener) info.service;
5412 listener.onNotificationRankingUpdate(rankingUpdate);
5413 } catch (RemoteException ex) {
5414 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
5418 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
5419 final INotificationListener listener = (INotificationListener) info.service;
5421 listener.onListenerHintsChanged(hints);
5422 } catch (RemoteException ex) {
5423 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
5427 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
5428 int interruptionFilter) {
5429 final INotificationListener listener = (INotificationListener) info.service;
5431 listener.onInterruptionFilterChanged(interruptionFilter);
5432 } catch (RemoteException ex) {
5433 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
5437 void notifyNotificationChannelChanged(ManagedServiceInfo info,
5438 final String pkg, final UserHandle user, final NotificationChannel channel,
5439 final int modificationType) {
5440 final INotificationListener listener = (INotificationListener) info.service;
5442 listener.onNotificationChannelModification(pkg, user, channel, modificationType);
5443 } catch (RemoteException ex) {
5444 Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
5448 private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
5449 final String pkg, final UserHandle user, final NotificationChannelGroup group,
5450 final int modificationType) {
5451 final INotificationListener listener = (INotificationListener) info.service;
5453 listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
5454 } catch (RemoteException ex) {
5455 Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
5459 public boolean isListenerPackage(String packageName) {
5460 if (packageName == null) {
5463 // TODO: clean up locking object later
5464 synchronized (mNotificationLock) {
5465 for (final ManagedServiceInfo serviceInfo : getServices()) {
5466 if (packageName.equals(serviceInfo.component.getPackageName())) {
5475 public static final class DumpFilter {
5476 public boolean filtered = false;
5477 public String pkgFilter;
5480 public boolean stats;
5481 public boolean redact = true;
5482 public boolean proto = false;
5484 public static DumpFilter parseFromArguments(String[] args) {
5485 final DumpFilter filter = new DumpFilter();
5486 for (int ai = 0; ai < args.length; ai++) {
5487 final String a = args[ai];
5488 if ("--proto".equals(args[0])) {
5489 filter.proto = true;
5491 if ("--noredact".equals(a) || "--reveal".equals(a)) {
5492 filter.redact = false;
5493 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
5494 if (ai < args.length-1) {
5496 filter.pkgFilter = args[ai].trim().toLowerCase();
5497 if (filter.pkgFilter.isEmpty()) {
5498 filter.pkgFilter = null;
5500 filter.filtered = true;
5503 } else if ("--zen".equals(a) || "zen".equals(a)) {
5504 filter.filtered = true;
5506 } else if ("--stats".equals(a)) {
5507 filter.stats = true;
5508 if (ai < args.length-1) {
5510 filter.since = Long.parseLong(args[ai]);
5519 public boolean matches(StatusBarNotification sbn) {
5520 if (!filtered) return true;
5521 return zen ? true : sbn != null
5522 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
5525 public boolean matches(ComponentName component) {
5526 if (!filtered) return true;
5527 return zen ? true : component != null && matches(component.getPackageName());
5530 public boolean matches(String pkg) {
5531 if (!filtered) return true;
5532 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
5536 public String toString() {
5537 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
5542 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
5543 * binder without sending large amounts of data over a oneway transaction.
5545 private static final class StatusBarNotificationHolder
5546 extends IStatusBarNotificationHolder.Stub {
5547 private StatusBarNotification mValue;
5549 public StatusBarNotificationHolder(StatusBarNotification value) {
5553 /** Get the held value and clear it. This function should only be called once per holder */
5555 public StatusBarNotification get() {
5556 StatusBarNotification value = mValue;
5562 private final class PolicyAccess {
5563 private static final String SEPARATOR = ":";
5564 private final String[] PERM = {
5565 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
5568 public boolean isPackageGranted(String pkg) {
5569 return pkg != null && getGrantedPackages().contains(pkg);
5572 public void put(String pkg, boolean granted) {
5573 if (pkg == null) return;
5574 final ArraySet<String> pkgs = getGrantedPackages();
5577 changed = pkgs.add(pkg);
5579 changed = pkgs.remove(pkg);
5581 if (!changed) return;
5582 final String setting = TextUtils.join(SEPARATOR, pkgs);
5583 final int currentUser = ActivityManager.getCurrentUser();
5584 Settings.Secure.putStringForUser(getContext().getContentResolver(),
5585 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5588 getContext().sendBroadcastAsUser(new Intent(NotificationManager
5589 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
5591 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
5594 public ArraySet<String> getGrantedPackages() {
5595 final ArraySet<String> pkgs = new ArraySet<>();
5597 long identity = Binder.clearCallingIdentity();
5599 final String setting = Settings.Secure.getStringForUser(
5600 getContext().getContentResolver(),
5601 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
5602 ActivityManager.getCurrentUser());
5603 if (setting != null) {
5604 final String[] tokens = setting.split(SEPARATOR);
5605 for (int i = 0; i < tokens.length; i++) {
5606 String token = tokens[i];
5607 if (token != null) {
5608 token = token.trim();
5610 if (TextUtils.isEmpty(token)) {
5617 Binder.restoreCallingIdentity(identity);
5622 public String[] getRequestingPackages() throws RemoteException {
5623 final ParceledListSlice list = mPackageManager
5624 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
5625 ActivityManager.getCurrentUser());
5626 final List<PackageInfo> pkgs = list.getList();
5627 if (pkgs == null || pkgs.isEmpty()) return new String[0];
5628 final int N = pkgs.size();
5629 final String[] rt = new String[N];
5630 for (int i = 0; i < N; i++) {
5631 rt[i] = pkgs.get(i).packageName;