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.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
20 import static android.service.notification.NotificationListenerService.TRIM_FULL;
21 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
22 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
23 import static org.xmlpull.v1.XmlPullParser.END_TAG;
24 import static org.xmlpull.v1.XmlPullParser.START_TAG;
26 import android.app.ActivityManager;
27 import android.app.ActivityManagerNative;
28 import android.app.AppGlobals;
29 import android.app.AppOpsManager;
30 import android.app.IActivityManager;
31 import android.app.INotificationManager;
32 import android.app.ITransientNotification;
33 import android.app.Notification;
34 import android.app.NotificationManager;
35 import android.app.NotificationManager.Policy;
36 import android.app.PendingIntent;
37 import android.app.StatusBarManager;
38 import android.app.backup.BackupManager;
39 import android.app.usage.UsageEvents;
40 import android.app.usage.UsageStatsManagerInternal;
41 import android.content.BroadcastReceiver;
42 import android.content.ComponentName;
43 import android.content.ContentResolver;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.pm.ApplicationInfo;
48 import android.content.pm.IPackageManager;
49 import android.content.pm.PackageInfo;
50 import android.content.pm.PackageManager;
51 import android.content.pm.PackageManager.NameNotFoundException;
52 import android.content.pm.ParceledListSlice;
53 import android.content.pm.UserInfo;
54 import android.content.res.Resources;
55 import android.database.ContentObserver;
56 import android.media.AudioAttributes;
57 import android.media.AudioManager;
58 import android.media.AudioManagerInternal;
59 import android.media.AudioSystem;
60 import android.media.IRingtonePlayer;
61 import android.net.Uri;
62 import android.os.Binder;
63 import android.os.Build;
64 import android.os.Bundle;
65 import android.os.Environment;
66 import android.os.Handler;
67 import android.os.HandlerThread;
68 import android.os.IBinder;
69 import android.os.IInterface;
70 import android.os.Looper;
71 import android.os.Message;
72 import android.os.Process;
73 import android.os.RemoteException;
74 import android.os.SystemProperties;
75 import android.os.UserHandle;
76 import android.os.UserManager;
77 import android.os.Vibrator;
78 import android.provider.Settings;
79 import android.service.notification.Condition;
80 import android.service.notification.IConditionListener;
81 import android.service.notification.IConditionProvider;
82 import android.service.notification.INotificationListener;
83 import android.service.notification.IStatusBarNotificationHolder;
84 import android.service.notification.NotificationListenerService;
85 import android.service.notification.NotificationRankingUpdate;
86 import android.service.notification.StatusBarNotification;
87 import android.service.notification.ZenModeConfig;
88 import android.telephony.PhoneStateListener;
89 import android.telephony.TelephonyManager;
90 import android.text.TextUtils;
91 import android.util.ArrayMap;
92 import android.util.ArraySet;
93 import android.util.AtomicFile;
94 import android.util.Log;
95 import android.util.Slog;
96 import android.util.Xml;
97 import android.view.accessibility.AccessibilityEvent;
98 import android.view.accessibility.AccessibilityManager;
99 import android.widget.Toast;
101 import com.android.internal.R;
102 import com.android.internal.statusbar.NotificationVisibility;
103 import com.android.internal.util.FastXmlSerializer;
104 import com.android.server.EventLogTags;
105 import com.android.server.LocalServices;
106 import com.android.server.SystemService;
107 import com.android.server.lights.Light;
108 import com.android.server.lights.LightsManager;
109 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
110 import com.android.server.notification.ManagedServices.UserProfiles;
111 import com.android.server.statusbar.StatusBarManagerInternal;
113 import libcore.io.IoUtils;
115 import org.json.JSONArray;
116 import org.json.JSONException;
117 import org.json.JSONObject;
118 import org.xmlpull.v1.XmlPullParser;
119 import org.xmlpull.v1.XmlPullParserException;
120 import org.xmlpull.v1.XmlSerializer;
122 import java.io.ByteArrayInputStream;
123 import java.io.ByteArrayOutputStream;
125 import java.io.FileDescriptor;
126 import java.io.FileInputStream;
127 import java.io.FileNotFoundException;
128 import java.io.FileOutputStream;
129 import java.io.IOException;
130 import java.io.InputStream;
131 import java.io.OutputStream;
132 import java.io.PrintWriter;
133 import java.nio.charset.StandardCharsets;
134 import java.util.ArrayDeque;
135 import java.util.ArrayList;
136 import java.util.HashSet;
137 import java.util.Iterator;
138 import java.util.List;
139 import java.util.Map.Entry;
140 import java.util.Objects;
143 public class NotificationManagerService extends SystemService {
144 static final String TAG = "NotificationService";
145 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
146 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
147 && SystemProperties.getBoolean("debug.child_notifs", false);
149 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
152 static final int MESSAGE_TIMEOUT = 2;
153 static final int MESSAGE_SAVE_POLICY_FILE = 3;
154 static final int MESSAGE_RECONSIDER_RANKING = 4;
155 static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
156 static final int MESSAGE_SEND_RANKING_UPDATE = 6;
157 static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
158 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
160 static final int LONG_DELAY = 3500; // 3.5 seconds
161 static final int SHORT_DELAY = 2000; // 2 seconds
163 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
165 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
167 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
168 static final boolean SCORE_ONGOING_HIGHER = false;
170 static final int JUNK_SCORE = -1000;
171 static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
172 static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
174 // Notifications with scores below this will not interrupt the user, either via LED or
175 // sound or vibration
176 static final int SCORE_INTERRUPTION_THRESHOLD =
177 Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
179 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
180 static final boolean ENABLE_BLOCKED_TOASTS = true;
182 // When #matchesCallFilter is called from the ringer, wait at most
183 // 3s to resolve the contacts. This timeout is required since
184 // ContactsProvider might take a long time to start up.
186 // Return STARRED_CONTACT when the timeout is hit in order to avoid
187 // missed calls in ZEN mode "Important".
188 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
189 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
190 ValidateNotificationPeople.STARRED_CONTACT;
192 /** notification_enqueue status value for a newly enqueued notification. */
193 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
195 /** notification_enqueue status value for an existing notification. */
196 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
198 /** notification_enqueue status value for an ignored notification. */
199 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
201 private IActivityManager mAm;
202 AudioManager mAudioManager;
203 AudioManagerInternal mAudioManagerInternal;
204 StatusBarManagerInternal mStatusBar;
207 final IBinder mForegroundToken = new Binder();
208 private WorkerHandler mHandler;
209 private final HandlerThread mRankingThread = new HandlerThread("ranker",
210 Process.THREAD_PRIORITY_BACKGROUND);
212 private Light mNotificationLight;
213 Light mAttentionLight;
214 private int mDefaultNotificationColor;
215 private int mDefaultNotificationLedOn;
217 private int mDefaultNotificationLedOff;
218 private long[] mDefaultVibrationPattern;
220 private long[] mFallbackVibrationPattern;
221 private boolean mUseAttentionLight;
222 boolean mSystemReady;
224 private boolean mDisableNotificationEffects;
225 private int mCallState;
226 private String mSoundNotificationKey;
227 private String mVibrateNotificationKey;
229 private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
230 private ComponentName mEffectsSuppressor;
231 private int mListenerHints; // right now, all hints are global
232 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
234 // for enabling and disabling notification pulse behavior
235 private boolean mScreenOn = true;
236 private boolean mInCall = false;
237 private boolean mNotificationPulseEnabled;
239 // used as a mutex for access to all active notifications & listeners
240 final ArrayList<NotificationRecord> mNotificationList =
241 new ArrayList<NotificationRecord>();
242 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
243 new ArrayMap<String, NotificationRecord>();
244 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
245 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
246 final PolicyAccess mPolicyAccess = new PolicyAccess();
248 // The last key in this list owns the hardware.
249 ArrayList<String> mLights = new ArrayList<>();
251 private AppOpsManager mAppOps;
252 private UsageStatsManagerInternal mAppUsageStats;
254 private Archive mArchive;
256 // Persistent storage for notification policy
257 private AtomicFile mPolicyFile;
259 // Temporary holder for <blocked-packages> config coming from old policy files.
260 private HashSet<String> mBlockedPackages = new HashSet<String>();
262 private static final int DB_VERSION = 1;
264 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
265 private static final String ATTR_VERSION = "version";
267 // Obsolete: converted if present, but not resaved to disk.
268 private static final String TAG_BLOCKED_PKGS = "blocked-packages";
269 private static final String TAG_PACKAGE = "package";
270 private static final String ATTR_NAME = "name";
272 private RankingHelper mRankingHelper;
274 private final UserProfiles mUserProfiles = new UserProfiles();
275 private NotificationListeners mListeners;
276 private ConditionProviders mConditionProviders;
277 private NotificationUsageStats mUsageStats;
279 private static final int MY_UID = Process.myUid();
280 private static final int MY_PID = Process.myPid();
281 private static final int REASON_DELEGATE_CLICK = 1;
282 private static final int REASON_DELEGATE_CANCEL = 2;
283 private static final int REASON_DELEGATE_CANCEL_ALL = 3;
284 private static final int REASON_DELEGATE_ERROR = 4;
285 private static final int REASON_PACKAGE_CHANGED = 5;
286 private static final int REASON_USER_STOPPED = 6;
287 private static final int REASON_PACKAGE_BANNED = 7;
288 private static final int REASON_NOMAN_CANCEL = 8;
289 private static final int REASON_NOMAN_CANCEL_ALL = 9;
290 private static final int REASON_LISTENER_CANCEL = 10;
291 private static final int REASON_LISTENER_CANCEL_ALL = 11;
292 private static final int REASON_GROUP_SUMMARY_CANCELED = 12;
293 private static final int REASON_GROUP_OPTIMIZATION = 13;
295 private static class Archive {
296 final int mBufferSize;
297 final ArrayDeque<StatusBarNotification> mBuffer;
299 public Archive(int size) {
301 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
304 public String toString() {
305 final StringBuilder sb = new StringBuilder();
306 final int N = mBuffer.size();
307 sb.append("Archive (");
309 sb.append(" notification");
310 sb.append((N==1)?")":"s)");
311 return sb.toString();
314 public void record(StatusBarNotification nr) {
315 if (mBuffer.size() == mBufferSize) {
316 mBuffer.removeFirst();
319 // We don't want to store the heavy bits of the notification in the archive,
320 // but other clients in the system process might be using the object, so we
321 // store a (lightened) copy.
322 mBuffer.addLast(nr.cloneLight());
325 public Iterator<StatusBarNotification> descendingIterator() {
326 return mBuffer.descendingIterator();
329 public StatusBarNotification[] getArray(int count) {
330 if (count == 0) count = mBufferSize;
331 final StatusBarNotification[] a
332 = new StatusBarNotification[Math.min(count, mBuffer.size())];
333 Iterator<StatusBarNotification> iter = descendingIterator();
335 while (iter.hasNext() && i < count) {
336 a[i++] = iter.next();
343 private void readPolicyXml(InputStream stream, boolean forRestore)
344 throws XmlPullParserException, NumberFormatException, IOException {
345 final XmlPullParser parser = Xml.newPullParser();
346 parser.setInput(stream, StandardCharsets.UTF_8.name());
350 int version = DB_VERSION;
351 while ((type = parser.next()) != END_DOCUMENT) {
352 tag = parser.getName();
353 if (type == START_TAG) {
354 if (TAG_NOTIFICATION_POLICY.equals(tag)) {
355 version = Integer.parseInt(
356 parser.getAttributeValue(null, ATTR_VERSION));
357 } else if (TAG_BLOCKED_PKGS.equals(tag)) {
358 while ((type = parser.next()) != END_DOCUMENT) {
359 tag = parser.getName();
360 if (TAG_PACKAGE.equals(tag)) {
361 mBlockedPackages.add(
362 parser.getAttributeValue(null, ATTR_NAME));
363 } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
369 mZenModeHelper.readXml(parser, forRestore);
370 mRankingHelper.readXml(parser, forRestore);
374 private void loadPolicyFile() {
375 if (DBG) Slog.d(TAG, "loadPolicyFile");
376 synchronized(mPolicyFile) {
377 mBlockedPackages.clear();
379 FileInputStream infile = null;
381 infile = mPolicyFile.openRead();
382 readPolicyXml(infile, false /*forRestore*/);
383 } catch (FileNotFoundException e) {
385 } catch (IOException e) {
386 Log.wtf(TAG, "Unable to read notification policy", e);
387 } catch (NumberFormatException e) {
388 Log.wtf(TAG, "Unable to parse notification policy", e);
389 } catch (XmlPullParserException e) {
390 Log.wtf(TAG, "Unable to parse notification policy", e);
392 IoUtils.closeQuietly(infile);
397 public void savePolicyFile() {
398 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
399 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
402 private void handleSavePolicyFile() {
403 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
404 synchronized (mPolicyFile) {
405 final FileOutputStream stream;
407 stream = mPolicyFile.startWrite();
408 } catch (IOException e) {
409 Slog.w(TAG, "Failed to save policy file", e);
414 writePolicyXml(stream, false /*forBackup*/);
415 mPolicyFile.finishWrite(stream);
416 } catch (IOException e) {
417 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
418 mPolicyFile.failWrite(stream);
421 BackupManager.dataChanged(getContext().getPackageName());
424 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
425 final XmlSerializer out = new FastXmlSerializer();
426 out.setOutput(stream, StandardCharsets.UTF_8.name());
427 out.startDocument(null, true);
428 out.startTag(null, TAG_NOTIFICATION_POLICY);
429 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
430 mZenModeHelper.writeXml(out, forBackup);
431 mRankingHelper.writeXml(out, forBackup);
432 out.endTag(null, TAG_NOTIFICATION_POLICY);
436 /** Use this when you actually want to post a notification or toast.
438 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
440 private boolean noteNotificationOp(String pkg, int uid) {
441 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
442 != AppOpsManager.MODE_ALLOWED) {
443 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
449 /** Use this to check if a package can post a notification or toast. */
450 private boolean checkNotificationOp(String pkg, int uid) {
451 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
452 == AppOpsManager.MODE_ALLOWED;
455 private static final class ToastRecord
459 final ITransientNotification callback;
462 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
466 this.callback = callback;
467 this.duration = duration;
470 void update(int duration) {
471 this.duration = duration;
474 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
475 if (filter != null && !filter.matches(pkg)) return;
476 pw.println(prefix + this);
480 public final String toString()
482 return "ToastRecord{"
483 + Integer.toHexString(System.identityHashCode(this))
485 + " callback=" + callback
486 + " duration=" + duration;
490 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
493 public void onSetDisabled(int status) {
494 synchronized (mNotificationList) {
495 mDisableNotificationEffects =
496 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
497 if (disableNotificationEffects(null) != null) {
498 // cancel whatever's going on
499 long identity = Binder.clearCallingIdentity();
501 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
502 if (player != null) {
505 } catch (RemoteException e) {
507 Binder.restoreCallingIdentity(identity);
510 identity = Binder.clearCallingIdentity();
514 Binder.restoreCallingIdentity(identity);
521 public void onClearAll(int callingUid, int callingPid, int userId) {
522 synchronized (mNotificationList) {
523 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
524 /*includeCurrentProfiles*/ true);
529 public void onNotificationClick(int callingUid, int callingPid, String key) {
530 synchronized (mNotificationList) {
531 NotificationRecord r = mNotificationsByKey.get(key);
533 Log.w(TAG, "No notification with key: " + key);
536 final long now = System.currentTimeMillis();
537 EventLogTags.writeNotificationClicked(key,
538 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
540 StatusBarNotification sbn = r.sbn;
541 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
542 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
543 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
544 REASON_DELEGATE_CLICK, null);
549 public void onNotificationActionClick(int callingUid, int callingPid, String key,
551 synchronized (mNotificationList) {
552 NotificationRecord r = mNotificationsByKey.get(key);
554 Log.w(TAG, "No notification with key: " + key);
557 final long now = System.currentTimeMillis();
558 EventLogTags.writeNotificationActionClicked(key, actionIndex,
559 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
560 // TODO: Log action click via UsageStats.
565 public void onNotificationClear(int callingUid, int callingPid,
566 String pkg, String tag, int id, int userId) {
567 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
568 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
569 true, userId, REASON_DELEGATE_CANCEL, null);
573 public void onPanelRevealed(boolean clearEffects, int items) {
574 EventLogTags.writeNotificationPanelRevealed(items);
581 public void onPanelHidden() {
582 EventLogTags.writeNotificationPanelHidden();
586 public void clearEffects() {
587 synchronized (mNotificationList) {
588 if (DBG) Slog.d(TAG, "clearEffects");
591 mSoundNotificationKey = null;
593 long identity = Binder.clearCallingIdentity();
595 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
596 if (player != null) {
599 } catch (RemoteException e) {
601 Binder.restoreCallingIdentity(identity);
605 mVibrateNotificationKey = null;
606 identity = Binder.clearCallingIdentity();
610 Binder.restoreCallingIdentity(identity);
615 updateLightsLocked();
620 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
621 int uid, int initialPid, String message, int userId) {
622 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
623 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
624 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
625 REASON_DELEGATE_ERROR, null);
626 long ident = Binder.clearCallingIdentity();
628 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
629 "Bad notification posted from package " + pkg
631 } catch (RemoteException e) {
633 Binder.restoreCallingIdentity(ident);
637 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
638 NotificationVisibility[] noLongerVisibleKeys) {
639 synchronized (mNotificationList) {
640 for (NotificationVisibility nv : newlyVisibleKeys) {
641 NotificationRecord r = mNotificationsByKey.get(nv.key);
642 if (r == null) continue;
643 r.setVisibility(true, nv.rank);
646 // Note that we might receive this event after notifications
647 // have already left the system, e.g. after dismissing from the
648 // shade. Hence not finding notifications in
649 // mNotificationsByKey is not an exceptional condition.
650 for (NotificationVisibility nv : noLongerVisibleKeys) {
651 NotificationRecord r = mNotificationsByKey.get(nv.key);
652 if (r == null) continue;
653 r.setVisibility(false, nv.rank);
660 public void onNotificationExpansionChanged(String key,
661 boolean userAction, boolean expanded) {
662 synchronized (mNotificationList) {
663 NotificationRecord r = mNotificationsByKey.get(key);
665 r.stats.onExpansionChanged(userAction, expanded);
666 final long now = System.currentTimeMillis();
667 EventLogTags.writeNotificationExpansion(key,
668 userAction ? 1 : 0, expanded ? 1 : 0,
669 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
675 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
677 public void onReceive(Context context, Intent intent) {
678 String action = intent.getAction();
679 if (action == null) {
683 boolean queryRestart = false;
684 boolean queryRemove = false;
685 boolean packageChanged = false;
686 boolean cancelNotifications = true;
688 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
689 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
690 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
691 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
692 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
693 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
694 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
695 UserHandle.USER_ALL);
696 String pkgList[] = null;
697 boolean queryReplace = queryRemove &&
698 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
699 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
700 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
701 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
702 } else if (queryRestart) {
703 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
705 Uri uri = intent.getData();
709 String pkgName = uri.getSchemeSpecificPart();
710 if (pkgName == null) {
713 if (packageChanged) {
714 // We cancel notifications for packages which have just been disabled
716 final IPackageManager pm = AppGlobals.getPackageManager();
717 final int enabled = pm.getApplicationEnabledSetting(pkgName,
718 changeUserId != UserHandle.USER_ALL ? changeUserId :
719 UserHandle.USER_OWNER);
720 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
721 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
722 cancelNotifications = false;
724 } catch (IllegalArgumentException e) {
725 // Package doesn't exist; probably racing with uninstall.
726 // cancelNotifications is already true, so nothing to do here.
728 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
730 } catch (RemoteException e) {
731 // Failed to talk to PackageManagerService Should never happen!
734 pkgList = new String[]{pkgName};
737 if (pkgList != null && (pkgList.length > 0)) {
738 for (String pkgName : pkgList) {
739 if (cancelNotifications) {
740 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
741 changeUserId, REASON_PACKAGE_CHANGED, null);
745 mListeners.onPackagesChanged(queryReplace, pkgList);
746 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
747 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
752 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
754 public void onReceive(Context context, Intent intent) {
755 String action = intent.getAction();
757 if (action.equals(Intent.ACTION_SCREEN_ON)) {
758 // Keep track of screen on/off state, but do not turn off the notification light
759 // until user passes through the lock screen or views the notification.
761 updateNotificationPulse();
762 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
764 updateNotificationPulse();
765 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
766 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
767 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
768 updateNotificationPulse();
769 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
770 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
771 if (userHandle >= 0) {
772 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
773 REASON_USER_STOPPED, null);
775 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
776 // turn off LED when user passes through lock screen
777 mNotificationLight.turnOff();
778 mStatusBar.notificationLightOff();
779 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
780 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
781 // reload per-user settings
782 mSettingsObserver.update(null);
783 mUserProfiles.updateCache(context);
784 // Refresh managed services
785 mConditionProviders.onUserSwitched(user);
786 mListeners.onUserSwitched(user);
787 mZenModeHelper.onUserSwitched(user);
788 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
789 mUserProfiles.updateCache(context);
790 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
791 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
792 mZenModeHelper.onUserRemoved(user);
797 private final class SettingsObserver extends ContentObserver {
798 private final Uri NOTIFICATION_LIGHT_PULSE_URI
799 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
801 SettingsObserver(Handler handler) {
806 ContentResolver resolver = getContext().getContentResolver();
807 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
808 false, this, UserHandle.USER_ALL);
812 @Override public void onChange(boolean selfChange, Uri uri) {
816 public void update(Uri uri) {
817 ContentResolver resolver = getContext().getContentResolver();
818 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
819 boolean pulseEnabled = Settings.System.getInt(resolver,
820 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
821 if (mNotificationPulseEnabled != pulseEnabled) {
822 mNotificationPulseEnabled = pulseEnabled;
823 updateNotificationPulse();
829 private SettingsObserver mSettingsObserver;
830 private ZenModeHelper mZenModeHelper;
832 private final Runnable mBuzzBeepBlinked = new Runnable() {
835 mStatusBar.buzzBeepBlinked();
839 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
840 int[] ar = r.getIntArray(resid);
844 final int len = ar.length > maxlen ? maxlen : ar.length;
845 long[] out = new long[len];
846 for (int i=0; i<len; i++) {
852 public NotificationManagerService(Context context) {
857 public void onStart() {
858 Resources resources = getContext().getResources();
860 mAm = ActivityManagerNative.getDefault();
861 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
862 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
863 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
865 mHandler = new WorkerHandler();
866 mRankingThread.start();
867 String[] extractorNames;
869 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
870 } catch (Resources.NotFoundException e) {
871 extractorNames = new String[0];
873 mUsageStats = new NotificationUsageStats(getContext());
874 mRankingHelper = new RankingHelper(getContext(),
875 new RankingWorkerHandler(mRankingThread.getLooper()),
878 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
879 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
880 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
882 public void onConfigChanged() {
887 void onZenModeChanged() {
888 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
889 synchronized(mNotificationList) {
890 updateInterruptionFilterLocked();
895 void onPolicyChanged() {
896 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
899 final File systemDir = new File(Environment.getDataDirectory(), "system");
900 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
904 mListeners = new NotificationListeners();
905 mStatusBar = getLocalService(StatusBarManagerInternal.class);
906 mStatusBar.setNotificationDelegate(mNotificationDelegate);
908 final LightsManager lights = getLocalService(LightsManager.class);
909 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
910 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
912 mDefaultNotificationColor = resources.getColor(
913 R.color.config_defaultNotificationColor);
914 mDefaultNotificationLedOn = resources.getInteger(
915 R.integer.config_defaultNotificationLedOn);
916 mDefaultNotificationLedOff = resources.getInteger(
917 R.integer.config_defaultNotificationLedOff);
919 mDefaultVibrationPattern = getLongArray(resources,
920 R.array.config_defaultNotificationVibePattern,
921 VIBRATE_PATTERN_MAXLEN,
922 DEFAULT_VIBRATE_PATTERN);
924 mFallbackVibrationPattern = getLongArray(resources,
925 R.array.config_notificationFallbackVibePattern,
926 VIBRATE_PATTERN_MAXLEN,
927 DEFAULT_VIBRATE_PATTERN);
929 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
931 // Don't start allowing notifications until the setup wizard has run once.
932 // After that, including subsequent boots, init with notifications turned on.
933 // This works on the first boot because the setup wizard will toggle this
934 // flag at least once and we'll go back to 0 after that.
935 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
936 Settings.Global.DEVICE_PROVISIONED, 0)) {
937 mDisableNotificationEffects = true;
939 mZenModeHelper.initZenMode();
940 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
942 mUserProfiles.updateCache(getContext());
943 listenForCallState();
945 // register for various Intents
946 IntentFilter filter = new IntentFilter();
947 filter.addAction(Intent.ACTION_SCREEN_ON);
948 filter.addAction(Intent.ACTION_SCREEN_OFF);
949 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
950 filter.addAction(Intent.ACTION_USER_PRESENT);
951 filter.addAction(Intent.ACTION_USER_STOPPED);
952 filter.addAction(Intent.ACTION_USER_SWITCHED);
953 filter.addAction(Intent.ACTION_USER_ADDED);
954 filter.addAction(Intent.ACTION_USER_REMOVED);
955 getContext().registerReceiver(mIntentReceiver, filter);
957 IntentFilter pkgFilter = new IntentFilter();
958 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
959 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
960 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
961 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
962 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
963 pkgFilter.addDataScheme("package");
964 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
967 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
968 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
971 mSettingsObserver = new SettingsObserver(mHandler);
973 mArchive = new Archive(resources.getInteger(
974 R.integer.config_notificationServiceArchiveSize));
976 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
977 publishLocalService(NotificationManagerInternal.class, mInternalService);
980 private void sendRegisteredOnlyBroadcast(String action) {
981 getContext().sendBroadcastAsUser(new Intent(action)
982 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
986 * Read the old XML-based app block database and import those blockages into the AppOps system.
988 private void importOldBlockDb() {
991 PackageManager pm = getContext().getPackageManager();
992 for (String pkg : mBlockedPackages) {
993 PackageInfo info = null;
995 info = pm.getPackageInfo(pkg, 0);
996 setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false);
997 } catch (NameNotFoundException e) {
1001 mBlockedPackages.clear();
1005 public void onBootPhase(int phase) {
1006 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1007 // no beeping until we're basically done booting
1008 mSystemReady = true;
1010 // Grab our optional AudioService
1011 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1012 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1013 mZenModeHelper.onSystemReady();
1014 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1015 // This observer will force an update when observe is called, causing us to
1016 // bind to listener services.
1017 mSettingsObserver.observe();
1018 mListeners.onBootPhaseAppsCanStart();
1019 mConditionProviders.onBootPhaseAppsCanStart();
1023 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1024 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1026 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1027 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1029 // Now, cancel any outstanding notifications that are part of a just-disabled app
1030 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1031 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1032 REASON_PACKAGE_BANNED, null);
1036 private void updateListenerHintsLocked() {
1037 final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
1038 if (hints == mListenerHints) return;
1039 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
1040 mListenerHints = hints;
1041 scheduleListenerHintsChanged(hints);
1044 private void updateEffectsSuppressorLocked() {
1045 final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
1046 ? mListenersDisablingEffects.valueAt(0).component : null;
1047 if (Objects.equals(suppressor, mEffectsSuppressor)) return;
1048 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
1049 mEffectsSuppressor = suppressor;
1050 mZenModeHelper.setEffectsSuppressed(suppressor != null);
1051 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1054 private void updateInterruptionFilterLocked() {
1055 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1056 if (interruptionFilter == mInterruptionFilter) return;
1057 mInterruptionFilter = interruptionFilter;
1058 scheduleInterruptionFilterChanged(interruptionFilter);
1061 private final IBinder mService = new INotificationManager.Stub() {
1063 // ============================================================================
1066 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1069 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1070 + " duration=" + duration);
1073 if (pkg == null || callback == null) {
1074 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1078 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1080 if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
1081 if (!isSystemToast) {
1082 Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
1087 synchronized (mToastQueue) {
1088 int callingPid = Binder.getCallingPid();
1089 long callingId = Binder.clearCallingIdentity();
1092 int index = indexOfToastLocked(pkg, callback);
1093 // If it's already in the queue, we update it in place, we don't
1094 // move it to the end of the queue.
1096 record = mToastQueue.get(index);
1097 record.update(duration);
1099 // Limit the number of toasts that any given package except the android
1100 // package can enqueue. Prevents DOS attacks and deals with leaks.
1101 if (!isSystemToast) {
1103 final int N = mToastQueue.size();
1104 for (int i=0; i<N; i++) {
1105 final ToastRecord r = mToastQueue.get(i);
1106 if (r.pkg.equals(pkg)) {
1108 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1109 Slog.e(TAG, "Package has already posted " + count
1110 + " toasts. Not showing more. Package=" + pkg);
1117 record = new ToastRecord(callingPid, pkg, callback, duration);
1118 mToastQueue.add(record);
1119 index = mToastQueue.size() - 1;
1120 keepProcessAliveLocked(callingPid);
1122 // If it's at index 0, it's the current toast. It doesn't matter if it's
1123 // new or just been updated. Call back and tell it to show itself.
1124 // If the callback fails, this will remove it from the list, so don't
1125 // assume that it's valid after this.
1127 showNextToastLocked();
1130 Binder.restoreCallingIdentity(callingId);
1136 public void cancelToast(String pkg, ITransientNotification callback) {
1137 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1139 if (pkg == null || callback == null) {
1140 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1144 synchronized (mToastQueue) {
1145 long callingId = Binder.clearCallingIdentity();
1147 int index = indexOfToastLocked(pkg, callback);
1149 cancelToastLocked(index);
1151 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1152 + " callback=" + callback);
1155 Binder.restoreCallingIdentity(callingId);
1161 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1162 Notification notification, int[] idOut, int userId) throws RemoteException {
1163 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1164 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1168 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1169 checkCallerIsSystemOrSameApp(pkg);
1170 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1171 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1172 // Don't allow client applications to cancel foreground service notis.
1173 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1174 Binder.getCallingUid() == Process.SYSTEM_UID
1175 ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
1180 public void cancelAllNotifications(String pkg, int userId) {
1181 checkCallerIsSystemOrSameApp(pkg);
1183 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1184 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1186 // Calling from user space, don't allow the canceling of actively
1187 // running foreground services.
1188 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1189 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1190 REASON_NOMAN_CANCEL_ALL, null);
1194 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1195 checkCallerIsSystem();
1197 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1201 * Use this when you just want to know if notifications are OK for this package.
1204 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1205 checkCallerIsSystem();
1206 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1207 == AppOpsManager.MODE_ALLOWED);
1211 public void setPackagePriority(String pkg, int uid, int priority) {
1212 checkCallerIsSystem();
1213 mRankingHelper.setPackagePriority(pkg, uid, priority);
1218 public int getPackagePriority(String pkg, int uid) {
1219 checkCallerIsSystem();
1220 return mRankingHelper.getPackagePriority(pkg, uid);
1224 public void setPackagePeekable(String pkg, int uid, boolean peekable) {
1225 checkCallerIsSystem();
1227 mRankingHelper.setPackagePeekable(pkg, uid, peekable);
1231 public boolean getPackagePeekable(String pkg, int uid) {
1232 checkCallerIsSystem();
1233 return mRankingHelper.getPackagePeekable(pkg, uid);
1237 public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
1238 checkCallerIsSystem();
1239 mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
1244 public int getPackageVisibilityOverride(String pkg, int uid) {
1245 checkCallerIsSystem();
1246 return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
1250 * System-only API for getting a list of current (i.e. not cleared) notifications.
1252 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1253 * @returns A list of all the notifications, in natural order.
1256 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1257 // enforce() will ensure the calling uid has the correct permission
1258 getContext().enforceCallingOrSelfPermission(
1259 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1260 "NotificationManagerService.getActiveNotifications");
1262 StatusBarNotification[] tmp = null;
1263 int uid = Binder.getCallingUid();
1265 // noteOp will check to make sure the callingPkg matches the uid
1266 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1267 == AppOpsManager.MODE_ALLOWED) {
1268 synchronized (mNotificationList) {
1269 tmp = new StatusBarNotification[mNotificationList.size()];
1270 final int N = mNotificationList.size();
1271 for (int i=0; i<N; i++) {
1272 tmp[i] = mNotificationList.get(i).sbn;
1280 * Public API for getting a list of current notifications for the calling package/uid.
1282 * @returns A list of all the package's notifications, in natural order.
1285 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1286 int incomingUserId) {
1287 checkCallerIsSystemOrSameApp(pkg);
1288 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1289 Binder.getCallingUid(), incomingUserId, true, false,
1290 "getAppActiveNotifications", pkg);
1292 final ArrayList<StatusBarNotification> list
1293 = new ArrayList<StatusBarNotification>(mNotificationList.size());
1295 synchronized (mNotificationList) {
1296 final int N = mNotificationList.size();
1297 for (int i = 0; i < N; i++) {
1298 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1299 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1300 // We could pass back a cloneLight() but clients might get confused and
1301 // try to send this thing back to notify() again, which would not work
1303 final StatusBarNotification sbnOut = new StatusBarNotification(
1304 sbn.getPackageName(),
1306 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1307 0, // hide score from apps
1308 sbn.getNotification().clone(),
1309 sbn.getUser(), sbn.getPostTime());
1315 return new ParceledListSlice<StatusBarNotification>(list);
1319 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1321 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1324 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1325 // enforce() will ensure the calling uid has the correct permission
1326 getContext().enforceCallingOrSelfPermission(
1327 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1328 "NotificationManagerService.getHistoricalNotifications");
1330 StatusBarNotification[] tmp = null;
1331 int uid = Binder.getCallingUid();
1333 // noteOp will check to make sure the callingPkg matches the uid
1334 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1335 == AppOpsManager.MODE_ALLOWED) {
1336 synchronized (mArchive) {
1337 tmp = mArchive.getArray(count);
1344 * Register a listener binder directly with the notification manager.
1346 * Only works with system callers. Apps should extend
1347 * {@link android.service.notification.NotificationListenerService}.
1350 public void registerListener(final INotificationListener listener,
1351 final ComponentName component, final int userid) {
1352 enforceSystemOrSystemUI("INotificationManager.registerListener");
1353 mListeners.registerService(listener, component, userid);
1357 * Remove a listener binder directly
1360 public void unregisterListener(INotificationListener listener, int userid) {
1361 mListeners.unregisterService(listener, userid);
1365 * Allow an INotificationListener to simulate a "clear all" operation.
1367 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1369 * @param token The binder for the listener, to check that the caller is allowed
1372 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1373 final int callingUid = Binder.getCallingUid();
1374 final int callingPid = Binder.getCallingPid();
1375 long identity = Binder.clearCallingIdentity();
1377 synchronized (mNotificationList) {
1378 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1380 final int N = keys.length;
1381 for (int i = 0; i < N; i++) {
1382 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1383 if (r == null) continue;
1384 final int userId = r.sbn.getUserId();
1385 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1386 !mUserProfiles.isCurrentProfile(userId)) {
1387 throw new SecurityException("Disallowed call from listener: "
1390 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1391 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1395 cancelAllLocked(callingUid, callingPid, info.userid,
1396 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1400 Binder.restoreCallingIdentity(identity);
1405 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1406 long identity = Binder.clearCallingIdentity();
1408 synchronized (mNotificationList) {
1409 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1411 final int N = keys.length;
1412 for (int i = 0; i < N; i++) {
1413 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1414 if (r == null) continue;
1415 final int userId = r.sbn.getUserId();
1416 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1417 !mUserProfiles.isCurrentProfile(userId)) {
1418 throw new SecurityException("Disallowed call from listener: "
1422 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1423 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1424 userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER
1426 UsageEvents.Event.USER_INTERACTION);
1433 Binder.restoreCallingIdentity(identity);
1437 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1438 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1439 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1440 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1442 userId, REASON_LISTENER_CANCEL, info);
1446 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1448 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1450 * @param token The binder for the listener, to check that the caller is allowed
1453 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1454 String tag, int id) {
1455 final int callingUid = Binder.getCallingUid();
1456 final int callingPid = Binder.getCallingPid();
1457 long identity = Binder.clearCallingIdentity();
1459 synchronized (mNotificationList) {
1460 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1461 if (info.supportsProfiles()) {
1462 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1463 + "from " + info.component
1464 + " use cancelNotification(key) instead.");
1466 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1467 pkg, tag, id, info.userid);
1471 Binder.restoreCallingIdentity(identity);
1476 * Allow an INotificationListener to request the list of outstanding notifications seen by
1477 * the current user. Useful when starting up, after which point the listener callbacks
1480 * @param token The binder for the listener, to check that the caller is allowed
1481 * @param keys An array of notification keys to fetch, or null to fetch everything
1482 * @returns The return value will contain the notifications specified in keys, in that
1483 * order, or if keys is null, all the notifications, in natural order.
1486 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1487 INotificationListener token, String[] keys, int trim) {
1488 synchronized (mNotificationList) {
1489 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1490 final boolean getKeys = keys != null;
1491 final int N = getKeys ? keys.length : mNotificationList.size();
1492 final ArrayList<StatusBarNotification> list
1493 = new ArrayList<StatusBarNotification>(N);
1494 for (int i=0; i<N; i++) {
1495 final NotificationRecord r = getKeys
1496 ? mNotificationsByKey.get(keys[i])
1497 : mNotificationList.get(i);
1498 if (r == null) continue;
1499 StatusBarNotification sbn = r.sbn;
1500 if (!isVisibleToListener(sbn, info)) continue;
1501 StatusBarNotification sbnToSend =
1502 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1503 list.add(sbnToSend);
1505 return new ParceledListSlice<StatusBarNotification>(list);
1510 public void requestHintsFromListener(INotificationListener token, int hints) {
1511 final long identity = Binder.clearCallingIdentity();
1513 synchronized (mNotificationList) {
1514 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1515 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1516 if (disableEffects) {
1517 mListenersDisablingEffects.add(info);
1519 mListenersDisablingEffects.remove(info);
1521 updateListenerHintsLocked();
1522 updateEffectsSuppressorLocked();
1525 Binder.restoreCallingIdentity(identity);
1530 public int getHintsFromListener(INotificationListener token) {
1531 synchronized (mNotificationList) {
1532 return mListenerHints;
1537 public void requestInterruptionFilterFromListener(INotificationListener token,
1538 int interruptionFilter) throws RemoteException {
1539 final long identity = Binder.clearCallingIdentity();
1541 synchronized (mNotificationList) {
1542 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1543 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1544 updateInterruptionFilterLocked();
1547 Binder.restoreCallingIdentity(identity);
1552 public int getInterruptionFilterFromListener(INotificationListener token)
1553 throws RemoteException {
1554 synchronized (mNotificationLight) {
1555 return mInterruptionFilter;
1560 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1561 throws RemoteException {
1562 synchronized (mNotificationList) {
1563 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1564 if (info == null) return;
1565 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1570 public int getZenMode() {
1571 return mZenModeHelper.getZenMode();
1575 public ZenModeConfig getZenModeConfig() {
1576 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1577 return mZenModeHelper.getConfig();
1581 public boolean setZenModeConfig(ZenModeConfig config, String reason) {
1582 checkCallerIsSystem();
1583 return mZenModeHelper.setConfig(config, reason);
1587 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1588 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1589 final long identity = Binder.clearCallingIdentity();
1591 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
1593 Binder.restoreCallingIdentity(identity);
1598 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1599 enforcePolicyAccess(pkg, "setInterruptionFilter");
1600 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1601 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1602 final long identity = Binder.clearCallingIdentity();
1604 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1606 Binder.restoreCallingIdentity(identity);
1611 public void notifyConditions(final String pkg, IConditionProvider provider,
1612 final Condition[] conditions) {
1613 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1614 checkCallerIsSystemOrSameApp(pkg);
1615 mHandler.post(new Runnable() {
1618 mConditionProviders.notifyConditions(pkg, info, conditions);
1624 public void requestZenModeConditions(IConditionListener callback, int relevance) {
1625 enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
1626 mZenModeHelper.requestZenModeConditions(callback, relevance);
1629 private void enforceSystemOrSystemUIOrVolume(String message) {
1630 if (mAudioManagerInternal != null) {
1631 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1632 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1636 enforceSystemOrSystemUI(message);
1639 private void enforceSystemOrSystemUI(String message) {
1640 if (isCallerSystem()) return;
1641 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1645 private void enforcePolicyAccess(String pkg, String method) {
1646 checkCallerIsSameApp(pkg);
1647 if (!checkPolicyAccess(pkg)) {
1648 Slog.w(TAG, "Notification policy access denied calling " + method);
1649 throw new SecurityException("Notification policy access denied");
1653 private boolean checkPackagePolicyAccess(String pkg) {
1654 return mPolicyAccess.isPackageGranted(pkg);
1657 private boolean checkPolicyAccess(String pkg) {
1658 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
1662 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1663 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1664 != PackageManager.PERMISSION_GRANTED) {
1665 pw.println("Permission Denial: can't dump NotificationManager from pid="
1666 + Binder.getCallingPid()
1667 + ", uid=" + Binder.getCallingUid());
1671 final DumpFilter filter = DumpFilter.parseFromArguments(args);
1672 if (filter != null && filter.stats) {
1673 dumpJson(pw, filter);
1675 dumpImpl(pw, filter);
1680 public ComponentName getEffectsSuppressor() {
1681 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
1682 return mEffectsSuppressor;
1686 public boolean matchesCallFilter(Bundle extras) {
1687 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
1688 return mZenModeHelper.matchesCallFilter(
1689 UserHandle.getCallingUserHandle(),
1691 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1692 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1693 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
1697 public boolean isSystemConditionProviderEnabled(String path) {
1698 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
1699 return mConditionProviders.isSystemProviderEnabled(path);
1702 // Backup/restore interface
1704 public byte[] getBackupPayload(int user) {
1705 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
1706 if (user != UserHandle.USER_OWNER) {
1707 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
1710 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1712 writePolicyXml(baos, true /*forBackup*/);
1713 return baos.toByteArray();
1714 } catch (IOException e) {
1715 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
1721 public void applyRestore(byte[] payload, int user) {
1722 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
1723 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
1724 if (payload == null) {
1725 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
1728 if (user != UserHandle.USER_OWNER) {
1729 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
1732 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
1734 readPolicyXml(bais, true /*forRestore*/);
1736 } catch (NumberFormatException | XmlPullParserException | IOException e) {
1737 Slog.w(TAG, "applyRestore: error reading payload", e);
1742 public boolean isNotificationPolicyAccessGranted(String pkg) {
1743 return checkPolicyAccess(pkg);
1747 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
1748 enforceSystemOrSystemUI("request policy access status for another package");
1749 return checkPackagePolicyAccess(pkg);
1753 public String[] getPackagesRequestingNotificationPolicyAccess()
1754 throws RemoteException {
1755 enforceSystemOrSystemUI("request policy access packages");
1756 final long identity = Binder.clearCallingIdentity();
1758 return mPolicyAccess.getRequestingPackages();
1760 Binder.restoreCallingIdentity(identity);
1765 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
1766 throws RemoteException {
1767 enforceSystemOrSystemUI("grant notification policy access");
1768 final long identity = Binder.clearCallingIdentity();
1770 synchronized (mNotificationList) {
1771 mPolicyAccess.put(pkg, granted);
1774 Binder.restoreCallingIdentity(identity);
1779 public Policy getNotificationPolicy(String pkg) {
1780 enforcePolicyAccess(pkg, "getNotificationPolicy");
1781 final long identity = Binder.clearCallingIdentity();
1783 return mZenModeHelper.getNotificationPolicy();
1785 Binder.restoreCallingIdentity(identity);
1790 public void setNotificationPolicy(String pkg, Policy policy) {
1791 enforcePolicyAccess(pkg, "setNotificationPolicy");
1792 final long identity = Binder.clearCallingIdentity();
1794 mZenModeHelper.setNotificationPolicy(policy);
1796 Binder.restoreCallingIdentity(identity);
1801 private String disableNotificationEffects(NotificationRecord record) {
1802 if (mDisableNotificationEffects) {
1803 return "booleanState";
1805 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1806 return "listenerHints";
1808 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1814 private void dumpJson(PrintWriter pw, DumpFilter filter) {
1815 JSONObject dump = new JSONObject();
1817 dump.put("service", "Notification Manager");
1818 JSONArray bans = new JSONArray();
1820 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1821 for (Integer userId : packageBans.keySet()) {
1822 for (String packageName : packageBans.get(userId)) {
1823 JSONObject ban = new JSONObject();
1824 ban.put("userId", userId);
1825 ban.put("packageName", packageName);
1829 } catch (NameNotFoundException e) {
1832 dump.put("bans", bans);
1833 dump.put("stats", mUsageStats.dumpJson(filter));
1834 } catch (JSONException e) {
1835 e.printStackTrace();
1840 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1841 pw.print("Current Notification Manager state");
1842 if (filter.filtered) {
1843 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
1847 final boolean zenOnly = filter.filtered && filter.zen;
1850 synchronized (mToastQueue) {
1851 N = mToastQueue.size();
1853 pw.println(" Toast Queue:");
1854 for (int i=0; i<N; i++) {
1855 mToastQueue.get(i).dump(pw, " ", filter);
1862 synchronized (mNotificationList) {
1864 N = mNotificationList.size();
1866 pw.println(" Notification List:");
1867 for (int i=0; i<N; i++) {
1868 final NotificationRecord nr = mNotificationList.get(i);
1869 if (filter.filtered && !filter.matches(nr.sbn)) continue;
1870 nr.dump(pw, " ", getContext(), filter.redact);
1875 if (!filter.filtered) {
1878 pw.println(" Lights List:");
1879 for (int i=0; i<N; i++) {
1885 pw.println(mLights.get(i));
1889 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
1890 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
1891 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
1892 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
1893 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
1894 pw.println(" mCallState=" + callStateToString(mCallState));
1895 pw.println(" mSystemReady=" + mSystemReady);
1897 pw.println(" mArchive=" + mArchive.toString());
1898 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1900 while (iter.hasNext()) {
1901 final StatusBarNotification sbn = iter.next();
1902 if (filter != null && !filter.matches(sbn)) continue;
1903 pw.println(" " + sbn);
1905 if (iter.hasNext()) pw.println(" ...");
1912 pw.println("\n Usage Stats:");
1913 mUsageStats.dump(pw, " ", filter);
1916 if (!filter.filtered || zenOnly) {
1917 pw.println("\n Zen Mode:");
1918 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
1919 mZenModeHelper.dump(pw, " ");
1921 pw.println("\n Zen Log:");
1922 ZenLog.dump(pw, " ");
1926 pw.println("\n Ranking Config:");
1927 mRankingHelper.dump(pw, " ", filter);
1929 pw.println("\n Notification listeners:");
1930 mListeners.dump(pw, filter);
1931 pw.print(" mListenerHints: "); pw.println(mListenerHints);
1932 pw.print(" mListenersDisablingEffects: (");
1933 N = mListenersDisablingEffects.size();
1934 for (int i = 0; i < N; i++) {
1935 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
1936 if (i > 0) pw.print(',');
1937 pw.print(listener.component);
1941 pw.println("\n Policy access:");
1942 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
1944 pw.println("\n Condition providers:");
1945 mConditionProviders.dump(pw, filter);
1947 pw.println("\n Group summaries:");
1948 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
1949 NotificationRecord r = entry.getValue();
1950 pw.println(" " + entry.getKey() + " -> " + r.getKey());
1951 if (mNotificationsByKey.get(r.getKey()) != r) {
1952 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
1953 r.dump(pw, " ", getContext(), filter.redact);
1958 pw.println("\n Banned Packages:");
1959 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1960 for (Integer userId : packageBans.keySet()) {
1961 for (String packageName : packageBans.get(userId)) {
1962 pw.println(" " + userId + ": " + packageName);
1965 } catch (NameNotFoundException e) {
1971 private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter)
1972 throws NameNotFoundException {
1973 ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>();
1974 ArrayList<String> packageNames = new ArrayList<>();
1975 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1976 final int userId = user.getUserHandle().getIdentifier();
1977 final PackageManager packageManager = getContext().getPackageManager();
1978 List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
1979 final int packageCount = packages.size();
1980 for (int p = 0; p < packageCount; p++) {
1981 final String packageName = packages.get(p).packageName;
1982 if (filter == null || filter.matches(packageName)) {
1983 final int uid = packageManager.getPackageUid(packageName, userId);
1984 if (!checkNotificationOp(packageName, uid)) {
1985 packageNames.add(packageName);
1989 if (!packageNames.isEmpty()) {
1990 packageBans.put(userId, packageNames);
1991 packageNames = new ArrayList<>();
1998 * The private API only accessible to the system process.
2000 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2002 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2003 String tag, int id, Notification notification, int[] idReceived, int userId) {
2004 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2005 idReceived, userId);
2009 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2011 checkCallerIsSystem();
2012 synchronized (mNotificationList) {
2013 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2015 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2016 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2019 NotificationRecord r = mNotificationList.get(i);
2020 StatusBarNotification sbn = r.sbn;
2021 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2022 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2023 // we have to revert to the flags we received initially *and* force remove
2024 // FLAG_FOREGROUND_SERVICE.
2025 sbn.getNotification().flags =
2026 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2027 mRankingHelper.sort(mNotificationList);
2028 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2033 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2034 final int callingPid, final String tag, final int id, final Notification notification,
2035 int[] idOut, int incomingUserId) {
2037 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2038 + " notification=" + notification);
2040 checkCallerIsSystemOrSameApp(pkg);
2041 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2042 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2044 final int userId = ActivityManager.handleIncomingUser(callingPid,
2045 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2046 final UserHandle user = new UserHandle(userId);
2048 // Limit the number of notifications that any given package except the android
2049 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2050 if (!isSystemNotification && !isNotificationFromListener) {
2051 synchronized (mNotificationList) {
2053 final int N = mNotificationList.size();
2054 for (int i=0; i<N; i++) {
2055 final NotificationRecord r = mNotificationList.get(i);
2056 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2057 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2058 break; // Allow updating existing notification
2061 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2062 Slog.e(TAG, "Package has already posted " + count
2063 + " notifications. Not showing more. package=" + pkg);
2071 if (pkg == null || notification == null) {
2072 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2073 + " id=" + id + " notification=" + notification);
2076 if (notification.getSmallIcon() != null) {
2077 if (!notification.isValid()) {
2078 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
2079 + " id=" + id + " notification=" + notification);
2083 mHandler.post(new Runnable() {
2087 synchronized (mNotificationList) {
2091 // 0. Sanitize inputs
2092 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2093 Notification.PRIORITY_MAX);
2094 // Migrate notification flags to scores
2095 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
2096 if (notification.priority < Notification.PRIORITY_MAX) {
2097 notification.priority = Notification.PRIORITY_MAX;
2099 } else if (SCORE_ONGOING_HIGHER &&
2100 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
2101 if (notification.priority < Notification.PRIORITY_HIGH) {
2102 notification.priority = Notification.PRIORITY_HIGH;
2105 // force no heads up per package config
2106 if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
2107 if (notification.extras == null) {
2108 notification.extras = new Bundle();
2110 notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
2111 Notification.HEADS_UP_NEVER);
2114 // 1. initial score: buckets of 10, around the app [-20..20]
2115 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
2117 // 2. extract ranking signals from the notification data
2118 final StatusBarNotification n = new StatusBarNotification(
2119 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
2121 NotificationRecord r = new NotificationRecord(n, score);
2122 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2124 // Retain ranking information from previous record
2125 r.copyRankingInformation(old);
2128 // Handle grouped notifications and bail out early if we
2129 // can to avoid extracting signals.
2130 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2131 boolean ignoreNotification =
2132 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
2134 // This conditional is a dirty hack to limit the logging done on
2135 // behalf of the download manager without affecting other apps.
2136 if (!pkg.equals("com.android.providers.downloads")
2137 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2138 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2139 if (ignoreNotification) {
2140 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2141 } else if (old != null) {
2142 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2144 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2145 pkg, id, tag, userId, notification.toString(),
2149 if (ignoreNotification) {
2153 mRankingHelper.extractSignals(r);
2155 // 3. Apply local rules
2158 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
2159 if (!isSystemNotification) {
2160 r.score = JUNK_SCORE;
2161 Slog.e(TAG, "Suppressing notification from package " + pkg
2162 + " by user request.");
2163 mUsageStats.registerBlocked(r);
2167 if (r.score < SCORE_DISPLAY_THRESHOLD) {
2168 // Notification will be blocked because the score is too low.
2172 int index = indexOfNotificationLocked(n.getKey());
2174 mNotificationList.add(r);
2175 mUsageStats.registerPostedByApp(r);
2177 old = mNotificationList.get(index);
2178 mNotificationList.set(index, r);
2179 mUsageStats.registerUpdatedByApp(r, old);
2180 // Make sure we don't lose the foreground service state.
2181 notification.flags |=
2182 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2186 mNotificationsByKey.put(n.getKey(), r);
2188 // Ensure if this is a foreground service that the proper additional
2190 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2191 notification.flags |= Notification.FLAG_ONGOING_EVENT
2192 | Notification.FLAG_NO_CLEAR;
2195 applyZenModeLocked(r);
2196 mRankingHelper.sort(mNotificationList);
2198 if (notification.getSmallIcon() != null) {
2199 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2200 mListeners.notifyPostedLocked(n, oldSbn);
2202 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2203 if (old != null && !old.isCanceled) {
2204 mListeners.notifyRemovedLocked(n);
2206 // ATTENTION: in a future release we will bail out here
2207 // so that we do not play sounds, show lights, etc. for invalid
2209 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2210 + n.getPackageName());
2213 buzzBeepBlinkLocked(r);
2222 * Ensures that grouped notification receive their special treatment.
2224 * <p>Cancels group children if the new notification causes a group to lose
2227 * <p>Updates mSummaryByGroupKey.</p>
2229 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2230 int callingUid, int callingPid) {
2231 StatusBarNotification sbn = r.sbn;
2232 Notification n = sbn.getNotification();
2233 String group = sbn.getGroupKey();
2234 boolean isSummary = n.isGroupSummary();
2236 Notification oldN = old != null ? old.sbn.getNotification() : null;
2237 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2238 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2241 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2242 if (removedSummary != old) {
2244 removedSummary != null ? removedSummary.getKey() : "<null>";
2245 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2246 ", removed=" + removedKey);
2250 mSummaryByGroupKey.put(group, r);
2253 // Clear out group children of the old notification if the update
2254 // causes the group summary to go away. This happens when the old
2255 // notification was a summary and the new one isn't, or when the old
2256 // notification was a summary and its group key changed.
2257 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2258 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2259 REASON_GROUP_SUMMARY_CANCELED);
2264 * Performs group notification optimizations if SysUI is the only active
2265 * notification listener and returns whether the given notification should
2268 * <p>Returns true if the given notification is a child of a group with a
2269 * summary, which means that SysUI will never show it, and hence the new
2270 * notification can be safely ignored. Also cancels any previous instance
2271 * of the ignored notification.</p>
2273 * <p>For summaries, cancels all children of that group, as SysUI will
2274 * never show them anymore.</p>
2276 * @return true if the given notification can be ignored as an optimization
2278 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
2279 NotificationRecord old, int callingUid, int callingPid) {
2280 if (!ENABLE_CHILD_NOTIFICATIONS) {
2281 // No optimizations are possible if listeners want groups.
2282 if (mListeners.notificationGroupsDesired()) {
2286 StatusBarNotification sbn = r.sbn;
2287 String group = sbn.getGroupKey();
2288 boolean isSummary = sbn.getNotification().isGroupSummary();
2289 boolean isChild = sbn.getNotification().isGroupChild();
2291 NotificationRecord summary = mSummaryByGroupKey.get(group);
2292 if (isChild && summary != null) {
2293 // Child with an active summary -> ignore
2295 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2296 + summary.getKey());
2298 // Make sure we don't leave an old version of the notification around.
2301 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2303 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2306 } else if (isSummary) {
2307 // Summary -> cancel children
2308 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2309 REASON_GROUP_OPTIMIZATION);
2315 private void buzzBeepBlinkLocked(NotificationRecord record) {
2316 boolean buzz = false;
2317 boolean beep = false;
2318 boolean blink = false;
2320 final Notification notification = record.sbn.getNotification();
2322 // Should this notification make noise, vibe, or use the LED?
2323 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
2324 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2325 if (DBG || record.isIntercepted())
2327 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2328 " intercept=" + record.isIntercepted()
2331 final int currentUser;
2332 final long token = Binder.clearCallingIdentity();
2334 currentUser = ActivityManager.getCurrentUser();
2336 Binder.restoreCallingIdentity(token);
2339 // If we're not supposed to beep, vibrate, etc. then don't.
2340 final String disableEffects = disableNotificationEffects(record);
2341 if (disableEffects != null) {
2342 ZenLog.traceDisableEffects(record, disableEffects);
2344 if (disableEffects == null
2345 && (!(record.isUpdate
2346 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2347 && (record.getUserId() == UserHandle.USER_ALL ||
2348 record.getUserId() == currentUser ||
2349 mUserProfiles.isCurrentProfile(record.getUserId()))
2352 && mAudioManager != null) {
2353 if (DBG) Slog.v(TAG, "Interrupting!");
2355 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2359 // should we use the default notification sound? (indicated either by
2360 // DEFAULT_SOUND or because notification.sound is pointing at
2361 // Settings.System.NOTIFICATION_SOUND)
2362 final boolean useDefaultSound =
2363 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2364 Settings.System.DEFAULT_NOTIFICATION_URI
2365 .equals(notification.sound);
2367 Uri soundUri = null;
2368 boolean hasValidSound = false;
2370 if (useDefaultSound) {
2371 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2373 // check to see if the default notification sound is silent
2374 ContentResolver resolver = getContext().getContentResolver();
2375 hasValidSound = Settings.System.getString(resolver,
2376 Settings.System.NOTIFICATION_SOUND) != null;
2377 } else if (notification.sound != null) {
2378 soundUri = notification.sound;
2379 hasValidSound = (soundUri != null);
2382 if (hasValidSound) {
2384 (notification.flags & Notification.FLAG_INSISTENT) != 0;
2385 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2386 mSoundNotificationKey = record.getKey();
2387 // do not play notifications if stream volume is 0 (typically because
2388 // ringer mode is silent) or if there is a user of exclusive audio focus
2389 if ((mAudioManager.getStreamVolume(
2390 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2391 && !mAudioManager.isAudioFocusExclusive()) {
2392 final long identity = Binder.clearCallingIdentity();
2394 final IRingtonePlayer player =
2395 mAudioManager.getRingtonePlayer();
2396 if (player != null) {
2397 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2398 + " with attributes " + audioAttributes);
2399 player.playAsync(soundUri, record.sbn.getUser(), looping,
2403 } catch (RemoteException e) {
2405 Binder.restoreCallingIdentity(identity);
2411 // Does the notification want to specify its own vibration?
2412 final boolean hasCustomVibrate = notification.vibrate != null;
2414 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2415 // mode, and no other vibration is specified, we fall back to vibration
2416 final boolean convertSoundToVibration =
2419 && (mAudioManager.getRingerModeInternal()
2420 == AudioManager.RINGER_MODE_VIBRATE);
2422 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2423 final boolean useDefaultVibrate =
2424 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2426 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2427 && !(mAudioManager.getRingerModeInternal()
2428 == AudioManager.RINGER_MODE_SILENT)) {
2429 mVibrateNotificationKey = record.getKey();
2431 if (useDefaultVibrate || convertSoundToVibration) {
2432 // Escalate privileges so we can use the vibrator even if the
2433 // notifying app does not have the VIBRATE permission.
2434 long identity = Binder.clearCallingIdentity();
2436 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2437 useDefaultVibrate ? mDefaultVibrationPattern
2438 : mFallbackVibrationPattern,
2439 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2440 ? 0: -1, audioAttributesForNotification(notification));
2443 Binder.restoreCallingIdentity(identity);
2445 } else if (notification.vibrate.length > 1) {
2446 // If you want your own vibration pattern, you need the VIBRATE
2448 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2449 notification.vibrate,
2450 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2451 ? 0: -1, audioAttributesForNotification(notification));
2458 // release the light
2459 boolean wasShowLights = mLights.remove(record.getKey());
2460 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
2461 mLights.add(record.getKey());
2462 updateLightsLocked();
2463 if (mUseAttentionLight) {
2464 mAttentionLight.pulse();
2467 } else if (wasShowLights) {
2468 updateLightsLocked();
2470 if (buzz || beep || blink) {
2471 EventLogTags.writeNotificationAlert(record.getKey(),
2472 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2473 mHandler.post(mBuzzBeepBlinked);
2477 private static AudioAttributes audioAttributesForNotification(Notification n) {
2478 if (n.audioAttributes != null
2479 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2480 // the audio attributes are set and different from the default, use them
2481 return n.audioAttributes;
2482 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2483 // the stream type is valid, use it
2484 return new AudioAttributes.Builder()
2485 .setInternalLegacyStreamType(n.audioStreamType)
2487 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2488 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2490 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2491 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2495 void showNextToastLocked() {
2496 ToastRecord record = mToastQueue.get(0);
2497 while (record != null) {
2498 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2500 record.callback.show();
2501 scheduleTimeoutLocked(record);
2503 } catch (RemoteException e) {
2504 Slog.w(TAG, "Object died trying to show notification " + record.callback
2505 + " in package " + record.pkg);
2506 // remove it from the list and let the process die
2507 int index = mToastQueue.indexOf(record);
2509 mToastQueue.remove(index);
2511 keepProcessAliveLocked(record.pid);
2512 if (mToastQueue.size() > 0) {
2513 record = mToastQueue.get(0);
2521 void cancelToastLocked(int index) {
2522 ToastRecord record = mToastQueue.get(index);
2524 record.callback.hide();
2525 } catch (RemoteException e) {
2526 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2527 + " in package " + record.pkg);
2528 // don't worry about this, we're about to remove it from
2531 mToastQueue.remove(index);
2532 keepProcessAliveLocked(record.pid);
2533 if (mToastQueue.size() > 0) {
2534 // Show the next one. If the callback fails, this will remove
2535 // it from the list, so don't assume that the list hasn't changed
2536 // after this point.
2537 showNextToastLocked();
2541 private void scheduleTimeoutLocked(ToastRecord r)
2543 mHandler.removeCallbacksAndMessages(r);
2544 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2545 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2546 mHandler.sendMessageDelayed(m, delay);
2549 private void handleTimeout(ToastRecord record)
2551 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2552 synchronized (mToastQueue) {
2553 int index = indexOfToastLocked(record.pkg, record.callback);
2555 cancelToastLocked(index);
2560 // lock on mToastQueue
2561 int indexOfToastLocked(String pkg, ITransientNotification callback)
2563 IBinder cbak = callback.asBinder();
2564 ArrayList<ToastRecord> list = mToastQueue;
2565 int len = list.size();
2566 for (int i=0; i<len; i++) {
2567 ToastRecord r = list.get(i);
2568 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2575 // lock on mToastQueue
2576 void keepProcessAliveLocked(int pid)
2578 int toastCount = 0; // toasts from this pid
2579 ArrayList<ToastRecord> list = mToastQueue;
2580 int N = list.size();
2581 for (int i=0; i<N; i++) {
2582 ToastRecord r = list.get(i);
2588 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2589 } catch (RemoteException e) {
2590 // Shouldn't happen.
2594 private void handleRankingReconsideration(Message message) {
2595 if (!(message.obj instanceof RankingReconsideration)) return;
2596 RankingReconsideration recon = (RankingReconsideration) message.obj;
2599 synchronized (mNotificationList) {
2600 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2601 if (record == null) {
2604 int indexBefore = findNotificationRecordIndexLocked(record);
2605 boolean interceptBefore = record.isIntercepted();
2606 int visibilityBefore = record.getPackageVisibilityOverride();
2607 recon.applyChangesLocked(record);
2608 applyZenModeLocked(record);
2609 mRankingHelper.sort(mNotificationList);
2610 int indexAfter = findNotificationRecordIndexLocked(record);
2611 boolean interceptAfter = record.isIntercepted();
2612 int visibilityAfter = record.getPackageVisibilityOverride();
2613 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2614 || visibilityBefore != visibilityAfter;
2615 if (interceptBefore && !interceptAfter) {
2616 buzzBeepBlinkLocked(record);
2620 scheduleSendRankingUpdate();
2624 private void handleRankingConfigChange() {
2625 synchronized (mNotificationList) {
2626 final int N = mNotificationList.size();
2627 ArrayList<String> orderBefore = new ArrayList<String>(N);
2628 int[] visibilities = new int[N];
2629 for (int i = 0; i < N; i++) {
2630 final NotificationRecord r = mNotificationList.get(i);
2631 orderBefore.add(r.getKey());
2632 visibilities[i] = r.getPackageVisibilityOverride();
2633 mRankingHelper.extractSignals(r);
2635 for (int i = 0; i < N; i++) {
2636 mRankingHelper.sort(mNotificationList);
2637 final NotificationRecord r = mNotificationList.get(i);
2638 if (!orderBefore.get(i).equals(r.getKey())
2639 || visibilities[i] != r.getPackageVisibilityOverride()) {
2640 scheduleSendRankingUpdate();
2647 // let zen mode evaluate this record
2648 private void applyZenModeLocked(NotificationRecord record) {
2649 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2652 // lock on mNotificationList
2653 private int findNotificationRecordIndexLocked(NotificationRecord target) {
2654 return mRankingHelper.indexOf(mNotificationList, target);
2657 private void scheduleSendRankingUpdate() {
2658 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2659 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2660 mHandler.sendMessage(m);
2663 private void handleSendRankingUpdate() {
2664 synchronized (mNotificationList) {
2665 mListeners.notifyRankingUpdateLocked();
2669 private void scheduleListenerHintsChanged(int state) {
2670 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2671 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2674 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2675 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2676 mHandler.obtainMessage(
2677 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2678 listenerInterruptionFilter,
2682 private void handleListenerHintsChanged(int hints) {
2683 synchronized (mNotificationList) {
2684 mListeners.notifyListenerHintsChangedLocked(hints);
2688 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2689 synchronized (mNotificationList) {
2690 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2694 private final class WorkerHandler extends Handler
2697 public void handleMessage(Message msg)
2701 case MESSAGE_TIMEOUT:
2702 handleTimeout((ToastRecord)msg.obj);
2704 case MESSAGE_SAVE_POLICY_FILE:
2705 handleSavePolicyFile();
2707 case MESSAGE_SEND_RANKING_UPDATE:
2708 handleSendRankingUpdate();
2710 case MESSAGE_LISTENER_HINTS_CHANGED:
2711 handleListenerHintsChanged(msg.arg1);
2713 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2714 handleListenerInterruptionFilterChanged(msg.arg1);
2721 private final class RankingWorkerHandler extends Handler
2723 public RankingWorkerHandler(Looper looper) {
2728 public void handleMessage(Message msg) {
2730 case MESSAGE_RECONSIDER_RANKING:
2731 handleRankingReconsideration(msg);
2733 case MESSAGE_RANKING_CONFIG_CHANGE:
2734 handleRankingConfigChange();
2741 // ============================================================================
2742 static int clamp(int x, int low, int high) {
2743 return (x < low) ? low : ((x > high) ? high : x);
2746 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2747 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2748 if (!manager.isEnabled()) {
2752 AccessibilityEvent event =
2753 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2754 event.setPackageName(packageName);
2755 event.setClassName(Notification.class.getName());
2756 event.setParcelableData(notification);
2757 CharSequence tickerText = notification.tickerText;
2758 if (!TextUtils.isEmpty(tickerText)) {
2759 event.getText().add(tickerText);
2762 manager.sendAccessibilityEvent(event);
2765 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2768 if (r.getNotification().deleteIntent != null) {
2770 r.getNotification().deleteIntent.send();
2771 } catch (PendingIntent.CanceledException ex) {
2772 // do nothing - there's no relevant way to recover, and
2773 // no reason to let this propagate
2774 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2780 if (r.getNotification().getSmallIcon() != null) {
2781 r.isCanceled = true;
2782 mListeners.notifyRemovedLocked(r.sbn);
2785 final String canceledKey = r.getKey();
2788 if (canceledKey.equals(mSoundNotificationKey)) {
2789 mSoundNotificationKey = null;
2790 final long identity = Binder.clearCallingIdentity();
2792 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2793 if (player != null) {
2796 } catch (RemoteException e) {
2798 Binder.restoreCallingIdentity(identity);
2803 if (canceledKey.equals(mVibrateNotificationKey)) {
2804 mVibrateNotificationKey = null;
2805 long identity = Binder.clearCallingIdentity();
2810 Binder.restoreCallingIdentity(identity);
2815 mLights.remove(canceledKey);
2817 // Record usage stats
2819 case REASON_DELEGATE_CANCEL:
2820 case REASON_DELEGATE_CANCEL_ALL:
2821 case REASON_LISTENER_CANCEL:
2822 case REASON_LISTENER_CANCEL_ALL:
2823 mUsageStats.registerDismissedByUser(r);
2825 case REASON_NOMAN_CANCEL:
2826 case REASON_NOMAN_CANCEL_ALL:
2827 mUsageStats.registerRemovedByApp(r);
2831 mNotificationsByKey.remove(r.sbn.getKey());
2832 String groupKey = r.getGroupKey();
2833 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2834 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2835 mSummaryByGroupKey.remove(groupKey);
2838 // Save it for users of getHistoricalNotifications()
2839 mArchive.record(r.sbn);
2841 final long now = System.currentTimeMillis();
2842 EventLogTags.writeNotificationCanceled(canceledKey, reason,
2843 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
2847 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2848 * and none of the {@code mustNotHaveFlags}.
2850 void cancelNotification(final int callingUid, final int callingPid,
2851 final String pkg, final String tag, final int id,
2852 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2853 final int userId, final int reason, final ManagedServiceInfo listener) {
2854 // In enqueueNotificationInternal notifications are added by scheduling the
2855 // work on the worker handler. Hence, we also schedule the cancel on this
2856 // handler to avoid a scenario where an add notification call followed by a
2857 // remove notification call ends up in not removing the notification.
2858 mHandler.post(new Runnable() {
2861 String listenerName = listener == null ? null : listener.component.toShortString();
2862 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
2863 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2865 synchronized (mNotificationList) {
2866 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2868 NotificationRecord r = mNotificationList.get(index);
2870 // Ideally we'd do this in the caller of this method. However, that would
2871 // require the caller to also find the notification.
2872 if (reason == REASON_DELEGATE_CLICK) {
2873 mUsageStats.registerClickedByUser(r);
2876 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2879 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2883 mNotificationList.remove(index);
2885 cancelNotificationLocked(r, sendDelete, reason);
2886 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2887 REASON_GROUP_SUMMARY_CANCELED);
2888 updateLightsLocked();
2896 * Determine whether the userId applies to the notification in question, either because
2897 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2899 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2901 // looking for USER_ALL notifications? match everything
2902 userId == UserHandle.USER_ALL
2903 // a notification sent to USER_ALL matches any query
2904 || r.getUserId() == UserHandle.USER_ALL
2905 // an exact user match
2906 || r.getUserId() == userId;
2910 * Determine whether the userId applies to the notification in question, either because
2911 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2912 * because it matches one of the users profiles.
2914 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2915 return notificationMatchesUserId(r, userId)
2916 || mUserProfiles.isCurrentProfile(r.getUserId());
2920 * Cancels all notifications from a given package that have all of the
2921 * {@code mustHaveFlags}.
2923 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2924 int mustNotHaveFlags, boolean doit, int userId, int reason,
2925 ManagedServiceInfo listener) {
2926 String listenerName = listener == null ? null : listener.component.toShortString();
2927 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2928 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2931 synchronized (mNotificationList) {
2932 final int N = mNotificationList.size();
2933 ArrayList<NotificationRecord> canceledNotifications = null;
2934 for (int i = N-1; i >= 0; --i) {
2935 NotificationRecord r = mNotificationList.get(i);
2936 if (!notificationMatchesUserId(r, userId)) {
2939 // Don't remove notifications to all, if there's no package name specified
2940 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2943 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2946 if ((r.getFlags() & mustNotHaveFlags) != 0) {
2949 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2952 if (canceledNotifications == null) {
2953 canceledNotifications = new ArrayList<>();
2955 canceledNotifications.add(r);
2959 mNotificationList.remove(i);
2960 cancelNotificationLocked(r, false, reason);
2962 if (doit && canceledNotifications != null) {
2963 final int M = canceledNotifications.size();
2964 for (int i = 0; i < M; i++) {
2965 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2966 listenerName, REASON_GROUP_SUMMARY_CANCELED);
2969 if (canceledNotifications != null) {
2970 updateLightsLocked();
2972 return canceledNotifications != null;
2976 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2977 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2978 String listenerName = listener == null ? null : listener.component.toShortString();
2979 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2980 null, userId, 0, 0, reason, listenerName);
2982 ArrayList<NotificationRecord> canceledNotifications = null;
2983 final int N = mNotificationList.size();
2984 for (int i=N-1; i>=0; i--) {
2985 NotificationRecord r = mNotificationList.get(i);
2986 if (includeCurrentProfiles) {
2987 if (!notificationMatchesCurrentProfiles(r, userId)) {
2991 if (!notificationMatchesUserId(r, userId)) {
2996 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2997 | Notification.FLAG_NO_CLEAR)) == 0) {
2998 mNotificationList.remove(i);
2999 cancelNotificationLocked(r, true, reason);
3000 // Make a note so we can cancel children later.
3001 if (canceledNotifications == null) {
3002 canceledNotifications = new ArrayList<>();
3004 canceledNotifications.add(r);
3007 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3008 for (int i = 0; i < M; i++) {
3009 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3010 listenerName, REASON_GROUP_SUMMARY_CANCELED);
3012 updateLightsLocked();
3015 // Warning: The caller is responsible for invoking updateLightsLocked().
3016 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3017 String listenerName, int reason) {
3018 Notification n = r.getNotification();
3019 if (!n.isGroupSummary()) {
3023 String pkg = r.sbn.getPackageName();
3024 int userId = r.getUserId();
3027 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3031 final int N = mNotificationList.size();
3032 for (int i = N - 1; i >= 0; i--) {
3033 NotificationRecord childR = mNotificationList.get(i);
3034 StatusBarNotification childSbn = childR.sbn;
3035 if (childR.getNotification().isGroupChild() &&
3036 childR.getGroupKey().equals(r.getGroupKey())) {
3037 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3038 childSbn.getTag(), userId, 0, 0, reason, listenerName);
3039 mNotificationList.remove(i);
3040 cancelNotificationLocked(childR, false, reason);
3045 // lock on mNotificationList
3046 void updateLightsLocked()
3048 // handle notification lights
3049 NotificationRecord ledNotification = null;
3050 while (ledNotification == null && !mLights.isEmpty()) {
3051 final String owner = mLights.get(mLights.size() - 1);
3052 ledNotification = mNotificationsByKey.get(owner);
3053 if (ledNotification == null) {
3054 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3055 mLights.remove(owner);
3059 // Don't flash while we are in a call or screen is on
3060 if (ledNotification == null || mInCall || mScreenOn) {
3061 mNotificationLight.turnOff();
3062 mStatusBar.notificationLightOff();
3064 final Notification ledno = ledNotification.sbn.getNotification();
3065 int ledARGB = ledno.ledARGB;
3066 int ledOnMS = ledno.ledOnMS;
3067 int ledOffMS = ledno.ledOffMS;
3068 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3069 ledARGB = mDefaultNotificationColor;
3070 ledOnMS = mDefaultNotificationLedOn;
3071 ledOffMS = mDefaultNotificationLedOff;
3073 if (mNotificationPulseEnabled) {
3075 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3078 // let SystemUI make an independent decision
3079 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3083 // lock on mNotificationList
3084 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3086 ArrayList<NotificationRecord> list = mNotificationList;
3087 final int len = list.size();
3088 for (int i=0; i<len; i++) {
3089 NotificationRecord r = list.get(i);
3090 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3091 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3098 // lock on mNotificationList
3099 int indexOfNotificationLocked(String key) {
3100 final int N = mNotificationList.size();
3101 for (int i = 0; i < N; i++) {
3102 if (key.equals(mNotificationList.get(i).getKey())) {
3109 private void updateNotificationPulse() {
3110 synchronized (mNotificationList) {
3111 updateLightsLocked();
3115 private static boolean isUidSystem(int uid) {
3116 final int appid = UserHandle.getAppId(uid);
3117 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3120 private static boolean isCallerSystem() {
3121 return isUidSystem(Binder.getCallingUid());
3124 private static void checkCallerIsSystem() {
3125 if (isCallerSystem()) {
3128 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3131 private static void checkCallerIsSystemOrSameApp(String pkg) {
3132 if (isCallerSystem()) {
3135 checkCallerIsSameApp(pkg);
3138 private static void checkCallerIsSameApp(String pkg) {
3139 final int uid = Binder.getCallingUid();
3141 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3142 pkg, 0, UserHandle.getCallingUserId());
3144 throw new SecurityException("Unknown package " + pkg);
3146 if (!UserHandle.isSameApp(ai.uid, uid)) {
3147 throw new SecurityException("Calling uid " + uid + " gave package"
3148 + pkg + " which is owned by uid " + ai.uid);
3150 } catch (RemoteException re) {
3151 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3155 private static String callStateToString(int state) {
3157 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3158 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3159 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3160 default: return "CALL_STATE_UNKNOWN_" + state;
3164 private void listenForCallState() {
3165 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3167 public void onCallStateChanged(int state, String incomingNumber) {
3168 if (mCallState == state) return;
3169 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3172 }, PhoneStateListener.LISTEN_CALL_STATE);
3176 * Generates a NotificationRankingUpdate from 'sbns', considering only
3177 * notifications visible to the given listener.
3179 * <p>Caller must hold a lock on mNotificationList.</p>
3181 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3182 int speedBumpIndex = -1;
3183 final int N = mNotificationList.size();
3184 ArrayList<String> keys = new ArrayList<String>(N);
3185 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3186 Bundle visibilityOverrides = new Bundle();
3187 for (int i = 0; i < N; i++) {
3188 NotificationRecord record = mNotificationList.get(i);
3189 if (!isVisibleToListener(record.sbn, info)) {
3192 keys.add(record.sbn.getKey());
3193 if (record.isIntercepted()) {
3194 interceptedKeys.add(record.sbn.getKey());
3196 if (record.getPackageVisibilityOverride()
3197 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3198 visibilityOverrides.putInt(record.sbn.getKey(),
3199 record.getPackageVisibilityOverride());
3201 // Find first min-prio notification for speedbump placement.
3202 if (speedBumpIndex == -1 &&
3203 // Intrusiveness trumps priority, hence ignore intrusives.
3204 !record.isRecentlyIntrusive() &&
3205 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
3206 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
3207 // (or lower as a safeguard) is sufficient to find the speedbump index.
3208 // We'll have to revisit this when more package priority buckets are introduced.
3209 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
3210 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
3211 speedBumpIndex = keys.size() - 1;
3214 String[] keysAr = keys.toArray(new String[keys.size()]);
3215 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3216 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3220 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3221 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3224 // TODO: remove this for older listeners.
3228 public class NotificationListeners extends ManagedServices {
3230 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3231 private boolean mNotificationGroupsDesired;
3233 public NotificationListeners() {
3234 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3238 protected Config getConfig() {
3239 Config c = new Config();
3240 c.caption = "notification listener";
3241 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3242 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3243 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3244 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3245 c.clientLabel = R.string.notification_listener_binding_label;
3250 protected IInterface asInterface(IBinder binder) {
3251 return INotificationListener.Stub.asInterface(binder);
3255 public void onServiceAdded(ManagedServiceInfo info) {
3256 final INotificationListener listener = (INotificationListener) info.service;
3257 final NotificationRankingUpdate update;
3258 synchronized (mNotificationList) {
3259 updateNotificationGroupsDesiredLocked();
3260 update = makeRankingUpdateLocked(info);
3263 listener.onListenerConnected(update);
3264 } catch (RemoteException e) {
3270 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3271 if (mListenersDisablingEffects.remove(removed)) {
3272 updateListenerHintsLocked();
3273 updateEffectsSuppressorLocked();
3275 mLightTrimListeners.remove(removed);
3276 updateNotificationGroupsDesiredLocked();
3279 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3280 if (trim == TRIM_LIGHT) {
3281 mLightTrimListeners.add(info);
3283 mLightTrimListeners.remove(info);
3287 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3288 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3293 * asynchronously notify all listeners about a new notification
3296 * Also takes care of removing a notification that has been visible to a listener before,
3297 * but isn't anymore.
3299 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
3300 // Lazily initialized snapshots of the notification.
3301 StatusBarNotification sbnClone = null;
3302 StatusBarNotification sbnCloneLight = null;
3304 for (final ManagedServiceInfo info : mServices) {
3305 boolean sbnVisible = isVisibleToListener(sbn, info);
3306 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3307 // This notification hasn't been and still isn't visible -> ignore.
3308 if (!oldSbnVisible && !sbnVisible) {
3311 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3313 // This notification became invisible -> remove the old one.
3314 if (oldSbnVisible && !sbnVisible) {
3315 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3316 mHandler.post(new Runnable() {
3319 notifyRemoved(info, oldSbnLightClone, update);
3325 final int trim = mListeners.getOnNotificationPostedTrim(info);
3327 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3328 sbnCloneLight = sbn.cloneLight();
3329 } else if (trim == TRIM_FULL && sbnClone == null) {
3330 sbnClone = sbn.clone();
3332 final StatusBarNotification sbnToPost =
3333 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3335 mHandler.post(new Runnable() {
3338 notifyPosted(info, sbnToPost, update);
3345 * asynchronously notify all listeners about a removed notification
3347 public void notifyRemovedLocked(StatusBarNotification sbn) {
3348 // make a copy in case changes are made to the underlying Notification object
3349 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3351 final StatusBarNotification sbnLight = sbn.cloneLight();
3352 for (final ManagedServiceInfo info : mServices) {
3353 if (!isVisibleToListener(sbn, info)) {
3356 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3357 mHandler.post(new Runnable() {
3360 notifyRemoved(info, sbnLight, update);
3367 * asynchronously notify all listeners about a reordering of notifications
3369 public void notifyRankingUpdateLocked() {
3370 for (final ManagedServiceInfo serviceInfo : mServices) {
3371 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3374 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
3375 mHandler.post(new Runnable() {
3378 notifyRankingUpdate(serviceInfo, update);
3384 public void notifyListenerHintsChangedLocked(final int hints) {
3385 for (final ManagedServiceInfo serviceInfo : mServices) {
3386 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3389 mHandler.post(new Runnable() {
3392 notifyListenerHintsChanged(serviceInfo, hints);
3398 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3399 for (final ManagedServiceInfo serviceInfo : mServices) {
3400 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3403 mHandler.post(new Runnable() {
3406 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3412 private void notifyPosted(final ManagedServiceInfo info,
3413 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
3414 final INotificationListener listener = (INotificationListener)info.service;
3415 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3417 listener.onNotificationPosted(sbnHolder, rankingUpdate);
3418 } catch (RemoteException ex) {
3419 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3423 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
3424 NotificationRankingUpdate rankingUpdate) {
3425 if (!info.enabledAndUserMatches(sbn.getUserId())) {
3428 final INotificationListener listener = (INotificationListener) info.service;
3429 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3431 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
3432 } catch (RemoteException ex) {
3433 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
3437 private void notifyRankingUpdate(ManagedServiceInfo info,
3438 NotificationRankingUpdate rankingUpdate) {
3439 final INotificationListener listener = (INotificationListener) info.service;
3441 listener.onNotificationRankingUpdate(rankingUpdate);
3442 } catch (RemoteException ex) {
3443 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3447 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
3448 final INotificationListener listener = (INotificationListener) info.service;
3450 listener.onListenerHintsChanged(hints);
3451 } catch (RemoteException ex) {
3452 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
3456 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3457 int interruptionFilter) {
3458 final INotificationListener listener = (INotificationListener) info.service;
3460 listener.onInterruptionFilterChanged(interruptionFilter);
3461 } catch (RemoteException ex) {
3462 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3466 private boolean isListenerPackage(String packageName) {
3467 if (packageName == null) {
3470 // TODO: clean up locking object later
3471 synchronized (mNotificationList) {
3472 for (final ManagedServiceInfo serviceInfo : mServices) {
3473 if (packageName.equals(serviceInfo.component.getPackageName())) {
3482 * Returns whether any of the currently registered listeners wants to receive notification
3485 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3487 public boolean notificationGroupsDesired() {
3488 return mNotificationGroupsDesired;
3491 private void updateNotificationGroupsDesiredLocked() {
3492 mNotificationGroupsDesired = true;
3493 // No listeners, no groups.
3494 if (mServices.isEmpty()) {
3495 mNotificationGroupsDesired = false;
3498 // One listener: Check whether it's SysUI.
3499 if (mServices.size() == 1 &&
3500 mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3501 mNotificationGroupsDesired = false;
3507 public static final class DumpFilter {
3508 public boolean filtered = false;
3509 public String pkgFilter;
3512 public boolean stats;
3513 public boolean redact = true;
3515 public static DumpFilter parseFromArguments(String[] args) {
3516 final DumpFilter filter = new DumpFilter();
3517 for (int ai = 0; ai < args.length; ai++) {
3518 final String a = args[ai];
3519 if ("--noredact".equals(a) || "--reveal".equals(a)) {
3520 filter.redact = false;
3521 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
3522 if (ai < args.length-1) {
3524 filter.pkgFilter = args[ai].trim().toLowerCase();
3525 if (filter.pkgFilter.isEmpty()) {
3526 filter.pkgFilter = null;
3528 filter.filtered = true;
3531 } else if ("--zen".equals(a) || "zen".equals(a)) {
3532 filter.filtered = true;
3534 } else if ("--stats".equals(a)) {
3535 filter.stats = true;
3536 if (ai < args.length-1) {
3538 filter.since = Long.valueOf(args[ai]);
3547 public boolean matches(StatusBarNotification sbn) {
3548 if (!filtered) return true;
3549 return zen ? true : sbn != null
3550 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
3553 public boolean matches(ComponentName component) {
3554 if (!filtered) return true;
3555 return zen ? true : component != null && matches(component.getPackageName());
3558 public boolean matches(String pkg) {
3559 if (!filtered) return true;
3560 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3564 public String toString() {
3565 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
3570 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3571 * binder without sending large amounts of data over a oneway transaction.
3573 private static final class StatusBarNotificationHolder
3574 extends IStatusBarNotificationHolder.Stub {
3575 private StatusBarNotification mValue;
3577 public StatusBarNotificationHolder(StatusBarNotification value) {
3581 /** Get the held value and clear it. This function should only be called once per holder */
3583 public StatusBarNotification get() {
3584 StatusBarNotification value = mValue;
3590 private final class PolicyAccess {
3591 private static final String SEPARATOR = ":";
3592 private final String[] PERM = {
3593 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
3596 public boolean isPackageGranted(String pkg) {
3597 return pkg != null && getGrantedPackages().contains(pkg);
3600 public void put(String pkg, boolean granted) {
3601 if (pkg == null) return;
3602 final ArraySet<String> pkgs = getGrantedPackages();
3605 changed = pkgs.add(pkg);
3607 changed = pkgs.remove(pkg);
3609 if (!changed) return;
3610 final String setting = TextUtils.join(SEPARATOR, pkgs);
3611 final int currentUser = ActivityManager.getCurrentUser();
3612 Settings.Secure.putStringForUser(getContext().getContentResolver(),
3613 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3616 getContext().sendBroadcastAsUser(new Intent(NotificationManager
3617 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
3619 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
3622 public ArraySet<String> getGrantedPackages() {
3623 final ArraySet<String> pkgs = new ArraySet<>();
3625 long identity = Binder.clearCallingIdentity();
3627 final String setting = Settings.Secure.getStringForUser(
3628 getContext().getContentResolver(),
3629 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3630 ActivityManager.getCurrentUser());
3631 if (setting != null) {
3632 final String[] tokens = setting.split(SEPARATOR);
3633 for (int i = 0; i < tokens.length; i++) {
3634 String token = tokens[i];
3635 if (token != null) {
3638 if (TextUtils.isEmpty(token)) {
3645 Binder.restoreCallingIdentity(identity);
3650 public String[] getRequestingPackages() throws RemoteException {
3651 final ParceledListSlice list = AppGlobals.getPackageManager()
3652 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
3653 ActivityManager.getCurrentUser());
3654 final List<PackageInfo> pkgs = list.getList();
3655 if (pkgs == null || pkgs.isEmpty()) return new String[0];
3656 final int N = pkgs.size();
3657 final String[] rt = new String[N];
3658 for (int i = 0; i < N; i++) {
3659 rt[i] = pkgs.get(i).packageName;