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 int N = mNotificationList.size();
1293 final ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(N);
1295 synchronized (mNotificationList) {
1296 for (int i = 0; i < N; i++) {
1297 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1298 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
1299 // We could pass back a cloneLight() but clients might get confused and
1300 // try to send this thing back to notify() again, which would not work
1302 final StatusBarNotification sbnOut = new StatusBarNotification(
1303 sbn.getPackageName(),
1305 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1306 0, // hide score from apps
1307 sbn.getNotification().clone(),
1308 sbn.getUser(), sbn.getPostTime());
1314 return new ParceledListSlice<StatusBarNotification>(list);
1318 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1320 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1323 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1324 // enforce() will ensure the calling uid has the correct permission
1325 getContext().enforceCallingOrSelfPermission(
1326 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1327 "NotificationManagerService.getHistoricalNotifications");
1329 StatusBarNotification[] tmp = null;
1330 int uid = Binder.getCallingUid();
1332 // noteOp will check to make sure the callingPkg matches the uid
1333 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1334 == AppOpsManager.MODE_ALLOWED) {
1335 synchronized (mArchive) {
1336 tmp = mArchive.getArray(count);
1343 * Register a listener binder directly with the notification manager.
1345 * Only works with system callers. Apps should extend
1346 * {@link android.service.notification.NotificationListenerService}.
1349 public void registerListener(final INotificationListener listener,
1350 final ComponentName component, final int userid) {
1351 enforceSystemOrSystemUI("INotificationManager.registerListener");
1352 mListeners.registerService(listener, component, userid);
1356 * Remove a listener binder directly
1359 public void unregisterListener(INotificationListener listener, int userid) {
1360 mListeners.unregisterService(listener, userid);
1364 * Allow an INotificationListener to simulate a "clear all" operation.
1366 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1368 * @param token The binder for the listener, to check that the caller is allowed
1371 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1372 final int callingUid = Binder.getCallingUid();
1373 final int callingPid = Binder.getCallingPid();
1374 long identity = Binder.clearCallingIdentity();
1376 synchronized (mNotificationList) {
1377 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1379 final int N = keys.length;
1380 for (int i = 0; i < N; i++) {
1381 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1382 if (r == null) continue;
1383 final int userId = r.sbn.getUserId();
1384 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1385 !mUserProfiles.isCurrentProfile(userId)) {
1386 throw new SecurityException("Disallowed call from listener: "
1389 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1390 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1394 cancelAllLocked(callingUid, callingPid, info.userid,
1395 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1399 Binder.restoreCallingIdentity(identity);
1404 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1405 long identity = Binder.clearCallingIdentity();
1407 synchronized (mNotificationList) {
1408 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1410 final int N = keys.length;
1411 for (int i = 0; i < N; i++) {
1412 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1413 if (r == null) continue;
1414 final int userId = r.sbn.getUserId();
1415 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1416 !mUserProfiles.isCurrentProfile(userId)) {
1417 throw new SecurityException("Disallowed call from listener: "
1421 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1422 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1423 userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER
1425 UsageEvents.Event.USER_INTERACTION);
1432 Binder.restoreCallingIdentity(identity);
1436 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1437 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1438 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1439 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1441 userId, REASON_LISTENER_CANCEL, info);
1445 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1447 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1449 * @param token The binder for the listener, to check that the caller is allowed
1452 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1453 String tag, int id) {
1454 final int callingUid = Binder.getCallingUid();
1455 final int callingPid = Binder.getCallingPid();
1456 long identity = Binder.clearCallingIdentity();
1458 synchronized (mNotificationList) {
1459 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1460 if (info.supportsProfiles()) {
1461 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1462 + "from " + info.component
1463 + " use cancelNotification(key) instead.");
1465 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1466 pkg, tag, id, info.userid);
1470 Binder.restoreCallingIdentity(identity);
1475 * Allow an INotificationListener to request the list of outstanding notifications seen by
1476 * the current user. Useful when starting up, after which point the listener callbacks
1479 * @param token The binder for the listener, to check that the caller is allowed
1480 * @param keys An array of notification keys to fetch, or null to fetch everything
1481 * @returns The return value will contain the notifications specified in keys, in that
1482 * order, or if keys is null, all the notifications, in natural order.
1485 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1486 INotificationListener token, String[] keys, int trim) {
1487 synchronized (mNotificationList) {
1488 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1489 final boolean getKeys = keys != null;
1490 final int N = getKeys ? keys.length : mNotificationList.size();
1491 final ArrayList<StatusBarNotification> list
1492 = new ArrayList<StatusBarNotification>(N);
1493 for (int i=0; i<N; i++) {
1494 final NotificationRecord r = getKeys
1495 ? mNotificationsByKey.get(keys[i])
1496 : mNotificationList.get(i);
1497 if (r == null) continue;
1498 StatusBarNotification sbn = r.sbn;
1499 if (!isVisibleToListener(sbn, info)) continue;
1500 StatusBarNotification sbnToSend =
1501 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1502 list.add(sbnToSend);
1504 return new ParceledListSlice<StatusBarNotification>(list);
1509 public void requestHintsFromListener(INotificationListener token, int hints) {
1510 final long identity = Binder.clearCallingIdentity();
1512 synchronized (mNotificationList) {
1513 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1514 final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
1515 if (disableEffects) {
1516 mListenersDisablingEffects.add(info);
1518 mListenersDisablingEffects.remove(info);
1520 updateListenerHintsLocked();
1521 updateEffectsSuppressorLocked();
1524 Binder.restoreCallingIdentity(identity);
1529 public int getHintsFromListener(INotificationListener token) {
1530 synchronized (mNotificationList) {
1531 return mListenerHints;
1536 public void requestInterruptionFilterFromListener(INotificationListener token,
1537 int interruptionFilter) throws RemoteException {
1538 final long identity = Binder.clearCallingIdentity();
1540 synchronized (mNotificationList) {
1541 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1542 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1543 updateInterruptionFilterLocked();
1546 Binder.restoreCallingIdentity(identity);
1551 public int getInterruptionFilterFromListener(INotificationListener token)
1552 throws RemoteException {
1553 synchronized (mNotificationLight) {
1554 return mInterruptionFilter;
1559 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1560 throws RemoteException {
1561 synchronized (mNotificationList) {
1562 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1563 if (info == null) return;
1564 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1569 public int getZenMode() {
1570 return mZenModeHelper.getZenMode();
1574 public ZenModeConfig getZenModeConfig() {
1575 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1576 return mZenModeHelper.getConfig();
1580 public boolean setZenModeConfig(ZenModeConfig config, String reason) {
1581 checkCallerIsSystem();
1582 return mZenModeHelper.setConfig(config, reason);
1586 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1587 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1588 final long identity = Binder.clearCallingIdentity();
1590 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
1592 Binder.restoreCallingIdentity(identity);
1597 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1598 enforcePolicyAccess(pkg, "setInterruptionFilter");
1599 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1600 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1601 final long identity = Binder.clearCallingIdentity();
1603 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1605 Binder.restoreCallingIdentity(identity);
1610 public void notifyConditions(final String pkg, IConditionProvider provider,
1611 final Condition[] conditions) {
1612 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1613 checkCallerIsSystemOrSameApp(pkg);
1614 mHandler.post(new Runnable() {
1617 mConditionProviders.notifyConditions(pkg, info, conditions);
1623 public void requestZenModeConditions(IConditionListener callback, int relevance) {
1624 enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
1625 mZenModeHelper.requestZenModeConditions(callback, relevance);
1628 private void enforceSystemOrSystemUIOrVolume(String message) {
1629 if (mAudioManagerInternal != null) {
1630 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1631 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1635 enforceSystemOrSystemUI(message);
1638 private void enforceSystemOrSystemUI(String message) {
1639 if (isCallerSystem()) return;
1640 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1644 private void enforcePolicyAccess(String pkg, String method) {
1645 checkCallerIsSameApp(pkg);
1646 if (!checkPolicyAccess(pkg)) {
1647 Slog.w(TAG, "Notification policy access denied calling " + method);
1648 throw new SecurityException("Notification policy access denied");
1652 private boolean checkPackagePolicyAccess(String pkg) {
1653 return mPolicyAccess.isPackageGranted(pkg);
1656 private boolean checkPolicyAccess(String pkg) {
1657 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
1661 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1662 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1663 != PackageManager.PERMISSION_GRANTED) {
1664 pw.println("Permission Denial: can't dump NotificationManager from pid="
1665 + Binder.getCallingPid()
1666 + ", uid=" + Binder.getCallingUid());
1670 final DumpFilter filter = DumpFilter.parseFromArguments(args);
1671 if (filter != null && filter.stats) {
1672 dumpJson(pw, filter);
1674 dumpImpl(pw, filter);
1679 public ComponentName getEffectsSuppressor() {
1680 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
1681 return mEffectsSuppressor;
1685 public boolean matchesCallFilter(Bundle extras) {
1686 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
1687 return mZenModeHelper.matchesCallFilter(
1688 UserHandle.getCallingUserHandle(),
1690 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
1691 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
1692 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
1696 public boolean isSystemConditionProviderEnabled(String path) {
1697 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
1698 return mConditionProviders.isSystemProviderEnabled(path);
1701 // Backup/restore interface
1703 public byte[] getBackupPayload(int user) {
1704 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
1705 if (user != UserHandle.USER_OWNER) {
1706 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
1709 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1711 writePolicyXml(baos, true /*forBackup*/);
1712 return baos.toByteArray();
1713 } catch (IOException e) {
1714 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
1720 public void applyRestore(byte[] payload, int user) {
1721 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
1722 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
1723 if (payload == null) {
1724 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
1727 if (user != UserHandle.USER_OWNER) {
1728 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
1731 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
1733 readPolicyXml(bais, true /*forRestore*/);
1735 } catch (NumberFormatException | XmlPullParserException | IOException e) {
1736 Slog.w(TAG, "applyRestore: error reading payload", e);
1741 public boolean isNotificationPolicyAccessGranted(String pkg) {
1742 return checkPolicyAccess(pkg);
1746 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
1747 enforceSystemOrSystemUI("request policy access status for another package");
1748 return checkPackagePolicyAccess(pkg);
1752 public String[] getPackagesRequestingNotificationPolicyAccess()
1753 throws RemoteException {
1754 enforceSystemOrSystemUI("request policy access packages");
1755 final long identity = Binder.clearCallingIdentity();
1757 return mPolicyAccess.getRequestingPackages();
1759 Binder.restoreCallingIdentity(identity);
1764 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
1765 throws RemoteException {
1766 enforceSystemOrSystemUI("grant notification policy access");
1767 final long identity = Binder.clearCallingIdentity();
1769 synchronized (mNotificationList) {
1770 mPolicyAccess.put(pkg, granted);
1773 Binder.restoreCallingIdentity(identity);
1778 public Policy getNotificationPolicy(String pkg) {
1779 enforcePolicyAccess(pkg, "getNotificationPolicy");
1780 final long identity = Binder.clearCallingIdentity();
1782 return mZenModeHelper.getNotificationPolicy();
1784 Binder.restoreCallingIdentity(identity);
1789 public void setNotificationPolicy(String pkg, Policy policy) {
1790 enforcePolicyAccess(pkg, "setNotificationPolicy");
1791 final long identity = Binder.clearCallingIdentity();
1793 mZenModeHelper.setNotificationPolicy(policy);
1795 Binder.restoreCallingIdentity(identity);
1800 private String disableNotificationEffects(NotificationRecord record) {
1801 if (mDisableNotificationEffects) {
1802 return "booleanState";
1804 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1805 return "listenerHints";
1807 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
1813 private void dumpJson(PrintWriter pw, DumpFilter filter) {
1814 JSONObject dump = new JSONObject();
1816 dump.put("service", "Notification Manager");
1817 JSONArray bans = new JSONArray();
1819 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1820 for (Integer userId : packageBans.keySet()) {
1821 for (String packageName : packageBans.get(userId)) {
1822 JSONObject ban = new JSONObject();
1823 ban.put("userId", userId);
1824 ban.put("packageName", packageName);
1828 } catch (NameNotFoundException e) {
1831 dump.put("bans", bans);
1832 dump.put("stats", mUsageStats.dumpJson(filter));
1833 } catch (JSONException e) {
1834 e.printStackTrace();
1839 void dumpImpl(PrintWriter pw, DumpFilter filter) {
1840 pw.print("Current Notification Manager state");
1841 if (filter.filtered) {
1842 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
1846 final boolean zenOnly = filter.filtered && filter.zen;
1849 synchronized (mToastQueue) {
1850 N = mToastQueue.size();
1852 pw.println(" Toast Queue:");
1853 for (int i=0; i<N; i++) {
1854 mToastQueue.get(i).dump(pw, " ", filter);
1861 synchronized (mNotificationList) {
1863 N = mNotificationList.size();
1865 pw.println(" Notification List:");
1866 for (int i=0; i<N; i++) {
1867 final NotificationRecord nr = mNotificationList.get(i);
1868 if (filter.filtered && !filter.matches(nr.sbn)) continue;
1869 nr.dump(pw, " ", getContext(), filter.redact);
1874 if (!filter.filtered) {
1877 pw.println(" Lights List:");
1878 for (int i=0; i<N; i++) {
1884 pw.println(mLights.get(i));
1888 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
1889 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
1890 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
1891 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
1892 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
1893 pw.println(" mCallState=" + callStateToString(mCallState));
1894 pw.println(" mSystemReady=" + mSystemReady);
1896 pw.println(" mArchive=" + mArchive.toString());
1897 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
1899 while (iter.hasNext()) {
1900 final StatusBarNotification sbn = iter.next();
1901 if (filter != null && !filter.matches(sbn)) continue;
1902 pw.println(" " + sbn);
1904 if (iter.hasNext()) pw.println(" ...");
1911 pw.println("\n Usage Stats:");
1912 mUsageStats.dump(pw, " ", filter);
1915 if (!filter.filtered || zenOnly) {
1916 pw.println("\n Zen Mode:");
1917 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
1918 mZenModeHelper.dump(pw, " ");
1920 pw.println("\n Zen Log:");
1921 ZenLog.dump(pw, " ");
1925 pw.println("\n Ranking Config:");
1926 mRankingHelper.dump(pw, " ", filter);
1928 pw.println("\n Notification listeners:");
1929 mListeners.dump(pw, filter);
1930 pw.print(" mListenerHints: "); pw.println(mListenerHints);
1931 pw.print(" mListenersDisablingEffects: (");
1932 N = mListenersDisablingEffects.size();
1933 for (int i = 0; i < N; i++) {
1934 final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
1935 if (i > 0) pw.print(',');
1936 pw.print(listener.component);
1940 pw.println("\n Policy access:");
1941 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
1943 pw.println("\n Condition providers:");
1944 mConditionProviders.dump(pw, filter);
1946 pw.println("\n Group summaries:");
1947 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
1948 NotificationRecord r = entry.getValue();
1949 pw.println(" " + entry.getKey() + " -> " + r.getKey());
1950 if (mNotificationsByKey.get(r.getKey()) != r) {
1951 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
1952 r.dump(pw, " ", getContext(), filter.redact);
1957 pw.println("\n Banned Packages:");
1958 ArrayMap<Integer, ArrayList<String>> packageBans = getPackageBans(filter);
1959 for (Integer userId : packageBans.keySet()) {
1960 for (String packageName : packageBans.get(userId)) {
1961 pw.println(" " + userId + ": " + packageName);
1964 } catch (NameNotFoundException e) {
1970 private ArrayMap<Integer, ArrayList<String>> getPackageBans(DumpFilter filter)
1971 throws NameNotFoundException {
1972 ArrayMap<Integer, ArrayList<String>> packageBans = new ArrayMap<>();
1973 ArrayList<String> packageNames = new ArrayList<>();
1974 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1975 final int userId = user.getUserHandle().getIdentifier();
1976 final PackageManager packageManager = getContext().getPackageManager();
1977 List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
1978 final int packageCount = packages.size();
1979 for (int p = 0; p < packageCount; p++) {
1980 final String packageName = packages.get(p).packageName;
1981 if (filter == null || filter.matches(packageName)) {
1982 final int uid = packageManager.getPackageUid(packageName, userId);
1983 if (!checkNotificationOp(packageName, uid)) {
1984 packageNames.add(packageName);
1988 if (!packageNames.isEmpty()) {
1989 packageBans.put(userId, packageNames);
1990 packageNames = new ArrayList<>();
1997 * The private API only accessible to the system process.
1999 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2001 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2002 String tag, int id, Notification notification, int[] idReceived, int userId) {
2003 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2004 idReceived, userId);
2008 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2010 checkCallerIsSystem();
2011 synchronized (mNotificationList) {
2012 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2014 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2015 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2018 NotificationRecord r = mNotificationList.get(i);
2019 StatusBarNotification sbn = r.sbn;
2020 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2021 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2022 // we have to revert to the flags we received initially *and* force remove
2023 // FLAG_FOREGROUND_SERVICE.
2024 sbn.getNotification().flags =
2025 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2026 mRankingHelper.sort(mNotificationList);
2027 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2032 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2033 final int callingPid, final String tag, final int id, final Notification notification,
2034 int[] idOut, int incomingUserId) {
2036 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2037 + " notification=" + notification);
2039 checkCallerIsSystemOrSameApp(pkg);
2040 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2041 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2043 final int userId = ActivityManager.handleIncomingUser(callingPid,
2044 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2045 final UserHandle user = new UserHandle(userId);
2047 // Limit the number of notifications that any given package except the android
2048 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2049 if (!isSystemNotification && !isNotificationFromListener) {
2050 synchronized (mNotificationList) {
2052 final int N = mNotificationList.size();
2053 for (int i=0; i<N; i++) {
2054 final NotificationRecord r = mNotificationList.get(i);
2055 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2056 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2057 break; // Allow updating existing notification
2060 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2061 Slog.e(TAG, "Package has already posted " + count
2062 + " notifications. Not showing more. package=" + pkg);
2070 if (pkg == null || notification == null) {
2071 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2072 + " id=" + id + " notification=" + notification);
2075 if (notification.getSmallIcon() != null) {
2076 if (!notification.isValid()) {
2077 throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
2078 + " id=" + id + " notification=" + notification);
2082 mHandler.post(new Runnable() {
2086 synchronized (mNotificationList) {
2090 // 0. Sanitize inputs
2091 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2092 Notification.PRIORITY_MAX);
2093 // Migrate notification flags to scores
2094 if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
2095 if (notification.priority < Notification.PRIORITY_MAX) {
2096 notification.priority = Notification.PRIORITY_MAX;
2098 } else if (SCORE_ONGOING_HIGHER &&
2099 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
2100 if (notification.priority < Notification.PRIORITY_HIGH) {
2101 notification.priority = Notification.PRIORITY_HIGH;
2104 // force no heads up per package config
2105 if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
2106 if (notification.extras == null) {
2107 notification.extras = new Bundle();
2109 notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
2110 Notification.HEADS_UP_NEVER);
2113 // 1. initial score: buckets of 10, around the app [-20..20]
2114 final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
2116 // 2. extract ranking signals from the notification data
2117 final StatusBarNotification n = new StatusBarNotification(
2118 pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
2120 NotificationRecord r = new NotificationRecord(n, score);
2121 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2123 // Retain ranking information from previous record
2124 r.copyRankingInformation(old);
2127 // Handle grouped notifications and bail out early if we
2128 // can to avoid extracting signals.
2129 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2130 boolean ignoreNotification =
2131 removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
2133 // This conditional is a dirty hack to limit the logging done on
2134 // behalf of the download manager without affecting other apps.
2135 if (!pkg.equals("com.android.providers.downloads")
2136 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2137 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2138 if (ignoreNotification) {
2139 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
2140 } else if (old != null) {
2141 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2143 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2144 pkg, id, tag, userId, notification.toString(),
2148 if (ignoreNotification) {
2152 mRankingHelper.extractSignals(r);
2154 // 3. Apply local rules
2157 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
2158 if (!isSystemNotification) {
2159 r.score = JUNK_SCORE;
2160 Slog.e(TAG, "Suppressing notification from package " + pkg
2161 + " by user request.");
2162 mUsageStats.registerBlocked(r);
2166 if (r.score < SCORE_DISPLAY_THRESHOLD) {
2167 // Notification will be blocked because the score is too low.
2171 int index = indexOfNotificationLocked(n.getKey());
2173 mNotificationList.add(r);
2174 mUsageStats.registerPostedByApp(r);
2176 old = mNotificationList.get(index);
2177 mNotificationList.set(index, r);
2178 mUsageStats.registerUpdatedByApp(r, old);
2179 // Make sure we don't lose the foreground service state.
2180 notification.flags |=
2181 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2185 mNotificationsByKey.put(n.getKey(), r);
2187 // Ensure if this is a foreground service that the proper additional
2189 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2190 notification.flags |= Notification.FLAG_ONGOING_EVENT
2191 | Notification.FLAG_NO_CLEAR;
2194 applyZenModeLocked(r);
2195 mRankingHelper.sort(mNotificationList);
2197 if (notification.getSmallIcon() != null) {
2198 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2199 mListeners.notifyPostedLocked(n, oldSbn);
2201 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2202 if (old != null && !old.isCanceled) {
2203 mListeners.notifyRemovedLocked(n);
2205 // ATTENTION: in a future release we will bail out here
2206 // so that we do not play sounds, show lights, etc. for invalid
2208 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2209 + n.getPackageName());
2212 buzzBeepBlinkLocked(r);
2221 * Ensures that grouped notification receive their special treatment.
2223 * <p>Cancels group children if the new notification causes a group to lose
2226 * <p>Updates mSummaryByGroupKey.</p>
2228 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2229 int callingUid, int callingPid) {
2230 StatusBarNotification sbn = r.sbn;
2231 Notification n = sbn.getNotification();
2232 String group = sbn.getGroupKey();
2233 boolean isSummary = n.isGroupSummary();
2235 Notification oldN = old != null ? old.sbn.getNotification() : null;
2236 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2237 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2240 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2241 if (removedSummary != old) {
2243 removedSummary != null ? removedSummary.getKey() : "<null>";
2244 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2245 ", removed=" + removedKey);
2249 mSummaryByGroupKey.put(group, r);
2252 // Clear out group children of the old notification if the update
2253 // causes the group summary to go away. This happens when the old
2254 // notification was a summary and the new one isn't, or when the old
2255 // notification was a summary and its group key changed.
2256 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2257 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2258 REASON_GROUP_SUMMARY_CANCELED);
2263 * Performs group notification optimizations if SysUI is the only active
2264 * notification listener and returns whether the given notification should
2267 * <p>Returns true if the given notification is a child of a group with a
2268 * summary, which means that SysUI will never show it, and hence the new
2269 * notification can be safely ignored. Also cancels any previous instance
2270 * of the ignored notification.</p>
2272 * <p>For summaries, cancels all children of that group, as SysUI will
2273 * never show them anymore.</p>
2275 * @return true if the given notification can be ignored as an optimization
2277 private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
2278 NotificationRecord old, int callingUid, int callingPid) {
2279 if (!ENABLE_CHILD_NOTIFICATIONS) {
2280 // No optimizations are possible if listeners want groups.
2281 if (mListeners.notificationGroupsDesired()) {
2285 StatusBarNotification sbn = r.sbn;
2286 String group = sbn.getGroupKey();
2287 boolean isSummary = sbn.getNotification().isGroupSummary();
2288 boolean isChild = sbn.getNotification().isGroupChild();
2290 NotificationRecord summary = mSummaryByGroupKey.get(group);
2291 if (isChild && summary != null) {
2292 // Child with an active summary -> ignore
2294 Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
2295 + summary.getKey());
2297 // Make sure we don't leave an old version of the notification around.
2300 Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
2302 cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
2305 } else if (isSummary) {
2306 // Summary -> cancel children
2307 cancelGroupChildrenLocked(r, callingUid, callingPid, null,
2308 REASON_GROUP_OPTIMIZATION);
2314 private void buzzBeepBlinkLocked(NotificationRecord record) {
2315 boolean buzz = false;
2316 boolean beep = false;
2317 boolean blink = false;
2319 final Notification notification = record.sbn.getNotification();
2321 // Should this notification make noise, vibe, or use the LED?
2322 final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
2323 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2324 if (DBG || record.isIntercepted())
2326 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2327 " intercept=" + record.isIntercepted()
2330 final int currentUser;
2331 final long token = Binder.clearCallingIdentity();
2333 currentUser = ActivityManager.getCurrentUser();
2335 Binder.restoreCallingIdentity(token);
2338 // If we're not supposed to beep, vibrate, etc. then don't.
2339 final String disableEffects = disableNotificationEffects(record);
2340 if (disableEffects != null) {
2341 ZenLog.traceDisableEffects(record, disableEffects);
2343 if (disableEffects == null
2344 && (!(record.isUpdate
2345 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
2346 && (record.getUserId() == UserHandle.USER_ALL ||
2347 record.getUserId() == currentUser ||
2348 mUserProfiles.isCurrentProfile(record.getUserId()))
2351 && mAudioManager != null) {
2352 if (DBG) Slog.v(TAG, "Interrupting!");
2354 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2358 // should we use the default notification sound? (indicated either by
2359 // DEFAULT_SOUND or because notification.sound is pointing at
2360 // Settings.System.NOTIFICATION_SOUND)
2361 final boolean useDefaultSound =
2362 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2363 Settings.System.DEFAULT_NOTIFICATION_URI
2364 .equals(notification.sound);
2366 Uri soundUri = null;
2367 boolean hasValidSound = false;
2369 if (useDefaultSound) {
2370 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2372 // check to see if the default notification sound is silent
2373 ContentResolver resolver = getContext().getContentResolver();
2374 hasValidSound = Settings.System.getString(resolver,
2375 Settings.System.NOTIFICATION_SOUND) != null;
2376 } else if (notification.sound != null) {
2377 soundUri = notification.sound;
2378 hasValidSound = (soundUri != null);
2381 if (hasValidSound) {
2383 (notification.flags & Notification.FLAG_INSISTENT) != 0;
2384 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2385 mSoundNotificationKey = record.getKey();
2386 // do not play notifications if stream volume is 0 (typically because
2387 // ringer mode is silent) or if there is a user of exclusive audio focus
2388 if ((mAudioManager.getStreamVolume(
2389 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2390 && !mAudioManager.isAudioFocusExclusive()) {
2391 final long identity = Binder.clearCallingIdentity();
2393 final IRingtonePlayer player =
2394 mAudioManager.getRingtonePlayer();
2395 if (player != null) {
2396 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2397 + " with attributes " + audioAttributes);
2398 player.playAsync(soundUri, record.sbn.getUser(), looping,
2402 } catch (RemoteException e) {
2404 Binder.restoreCallingIdentity(identity);
2410 // Does the notification want to specify its own vibration?
2411 final boolean hasCustomVibrate = notification.vibrate != null;
2413 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2414 // mode, and no other vibration is specified, we fall back to vibration
2415 final boolean convertSoundToVibration =
2418 && (mAudioManager.getRingerModeInternal()
2419 == AudioManager.RINGER_MODE_VIBRATE);
2421 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2422 final boolean useDefaultVibrate =
2423 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2425 if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
2426 && !(mAudioManager.getRingerModeInternal()
2427 == AudioManager.RINGER_MODE_SILENT)) {
2428 mVibrateNotificationKey = record.getKey();
2430 if (useDefaultVibrate || convertSoundToVibration) {
2431 // Escalate privileges so we can use the vibrator even if the
2432 // notifying app does not have the VIBRATE permission.
2433 long identity = Binder.clearCallingIdentity();
2435 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2436 useDefaultVibrate ? mDefaultVibrationPattern
2437 : mFallbackVibrationPattern,
2438 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2439 ? 0: -1, audioAttributesForNotification(notification));
2442 Binder.restoreCallingIdentity(identity);
2444 } else if (notification.vibrate.length > 1) {
2445 // If you want your own vibration pattern, you need the VIBRATE
2447 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2448 notification.vibrate,
2449 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2450 ? 0: -1, audioAttributesForNotification(notification));
2457 // release the light
2458 boolean wasShowLights = mLights.remove(record.getKey());
2459 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
2460 mLights.add(record.getKey());
2461 updateLightsLocked();
2462 if (mUseAttentionLight) {
2463 mAttentionLight.pulse();
2466 } else if (wasShowLights) {
2467 updateLightsLocked();
2469 if (buzz || beep || blink) {
2470 EventLogTags.writeNotificationAlert(record.getKey(),
2471 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2472 mHandler.post(mBuzzBeepBlinked);
2476 private static AudioAttributes audioAttributesForNotification(Notification n) {
2477 if (n.audioAttributes != null
2478 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2479 // the audio attributes are set and different from the default, use them
2480 return n.audioAttributes;
2481 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2482 // the stream type is valid, use it
2483 return new AudioAttributes.Builder()
2484 .setInternalLegacyStreamType(n.audioStreamType)
2486 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2487 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2489 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2490 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2494 void showNextToastLocked() {
2495 ToastRecord record = mToastQueue.get(0);
2496 while (record != null) {
2497 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2499 record.callback.show();
2500 scheduleTimeoutLocked(record);
2502 } catch (RemoteException e) {
2503 Slog.w(TAG, "Object died trying to show notification " + record.callback
2504 + " in package " + record.pkg);
2505 // remove it from the list and let the process die
2506 int index = mToastQueue.indexOf(record);
2508 mToastQueue.remove(index);
2510 keepProcessAliveLocked(record.pid);
2511 if (mToastQueue.size() > 0) {
2512 record = mToastQueue.get(0);
2520 void cancelToastLocked(int index) {
2521 ToastRecord record = mToastQueue.get(index);
2523 record.callback.hide();
2524 } catch (RemoteException e) {
2525 Slog.w(TAG, "Object died trying to hide notification " + record.callback
2526 + " in package " + record.pkg);
2527 // don't worry about this, we're about to remove it from
2530 mToastQueue.remove(index);
2531 keepProcessAliveLocked(record.pid);
2532 if (mToastQueue.size() > 0) {
2533 // Show the next one. If the callback fails, this will remove
2534 // it from the list, so don't assume that the list hasn't changed
2535 // after this point.
2536 showNextToastLocked();
2540 private void scheduleTimeoutLocked(ToastRecord r)
2542 mHandler.removeCallbacksAndMessages(r);
2543 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
2544 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
2545 mHandler.sendMessageDelayed(m, delay);
2548 private void handleTimeout(ToastRecord record)
2550 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
2551 synchronized (mToastQueue) {
2552 int index = indexOfToastLocked(record.pkg, record.callback);
2554 cancelToastLocked(index);
2559 // lock on mToastQueue
2560 int indexOfToastLocked(String pkg, ITransientNotification callback)
2562 IBinder cbak = callback.asBinder();
2563 ArrayList<ToastRecord> list = mToastQueue;
2564 int len = list.size();
2565 for (int i=0; i<len; i++) {
2566 ToastRecord r = list.get(i);
2567 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
2574 // lock on mToastQueue
2575 void keepProcessAliveLocked(int pid)
2577 int toastCount = 0; // toasts from this pid
2578 ArrayList<ToastRecord> list = mToastQueue;
2579 int N = list.size();
2580 for (int i=0; i<N; i++) {
2581 ToastRecord r = list.get(i);
2587 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
2588 } catch (RemoteException e) {
2589 // Shouldn't happen.
2593 private void handleRankingReconsideration(Message message) {
2594 if (!(message.obj instanceof RankingReconsideration)) return;
2595 RankingReconsideration recon = (RankingReconsideration) message.obj;
2598 synchronized (mNotificationList) {
2599 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
2600 if (record == null) {
2603 int indexBefore = findNotificationRecordIndexLocked(record);
2604 boolean interceptBefore = record.isIntercepted();
2605 int visibilityBefore = record.getPackageVisibilityOverride();
2606 recon.applyChangesLocked(record);
2607 applyZenModeLocked(record);
2608 mRankingHelper.sort(mNotificationList);
2609 int indexAfter = findNotificationRecordIndexLocked(record);
2610 boolean interceptAfter = record.isIntercepted();
2611 int visibilityAfter = record.getPackageVisibilityOverride();
2612 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
2613 || visibilityBefore != visibilityAfter;
2614 if (interceptBefore && !interceptAfter) {
2615 buzzBeepBlinkLocked(record);
2619 scheduleSendRankingUpdate();
2623 private void handleRankingConfigChange() {
2624 synchronized (mNotificationList) {
2625 final int N = mNotificationList.size();
2626 ArrayList<String> orderBefore = new ArrayList<String>(N);
2627 int[] visibilities = new int[N];
2628 for (int i = 0; i < N; i++) {
2629 final NotificationRecord r = mNotificationList.get(i);
2630 orderBefore.add(r.getKey());
2631 visibilities[i] = r.getPackageVisibilityOverride();
2632 mRankingHelper.extractSignals(r);
2634 for (int i = 0; i < N; i++) {
2635 mRankingHelper.sort(mNotificationList);
2636 final NotificationRecord r = mNotificationList.get(i);
2637 if (!orderBefore.get(i).equals(r.getKey())
2638 || visibilities[i] != r.getPackageVisibilityOverride()) {
2639 scheduleSendRankingUpdate();
2646 // let zen mode evaluate this record
2647 private void applyZenModeLocked(NotificationRecord record) {
2648 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
2651 // lock on mNotificationList
2652 private int findNotificationRecordIndexLocked(NotificationRecord target) {
2653 return mRankingHelper.indexOf(mNotificationList, target);
2656 private void scheduleSendRankingUpdate() {
2657 mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
2658 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
2659 mHandler.sendMessage(m);
2662 private void handleSendRankingUpdate() {
2663 synchronized (mNotificationList) {
2664 mListeners.notifyRankingUpdateLocked();
2668 private void scheduleListenerHintsChanged(int state) {
2669 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
2670 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
2673 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
2674 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
2675 mHandler.obtainMessage(
2676 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
2677 listenerInterruptionFilter,
2681 private void handleListenerHintsChanged(int hints) {
2682 synchronized (mNotificationList) {
2683 mListeners.notifyListenerHintsChangedLocked(hints);
2687 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
2688 synchronized (mNotificationList) {
2689 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
2693 private final class WorkerHandler extends Handler
2696 public void handleMessage(Message msg)
2700 case MESSAGE_TIMEOUT:
2701 handleTimeout((ToastRecord)msg.obj);
2703 case MESSAGE_SAVE_POLICY_FILE:
2704 handleSavePolicyFile();
2706 case MESSAGE_SEND_RANKING_UPDATE:
2707 handleSendRankingUpdate();
2709 case MESSAGE_LISTENER_HINTS_CHANGED:
2710 handleListenerHintsChanged(msg.arg1);
2712 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
2713 handleListenerInterruptionFilterChanged(msg.arg1);
2720 private final class RankingWorkerHandler extends Handler
2722 public RankingWorkerHandler(Looper looper) {
2727 public void handleMessage(Message msg) {
2729 case MESSAGE_RECONSIDER_RANKING:
2730 handleRankingReconsideration(msg);
2732 case MESSAGE_RANKING_CONFIG_CHANGE:
2733 handleRankingConfigChange();
2740 // ============================================================================
2741 static int clamp(int x, int low, int high) {
2742 return (x < low) ? low : ((x > high) ? high : x);
2745 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
2746 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
2747 if (!manager.isEnabled()) {
2751 AccessibilityEvent event =
2752 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
2753 event.setPackageName(packageName);
2754 event.setClassName(Notification.class.getName());
2755 event.setParcelableData(notification);
2756 CharSequence tickerText = notification.tickerText;
2757 if (!TextUtils.isEmpty(tickerText)) {
2758 event.getText().add(tickerText);
2761 manager.sendAccessibilityEvent(event);
2764 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
2767 if (r.getNotification().deleteIntent != null) {
2769 r.getNotification().deleteIntent.send();
2770 } catch (PendingIntent.CanceledException ex) {
2771 // do nothing - there's no relevant way to recover, and
2772 // no reason to let this propagate
2773 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
2779 if (r.getNotification().getSmallIcon() != null) {
2780 r.isCanceled = true;
2781 mListeners.notifyRemovedLocked(r.sbn);
2784 final String canceledKey = r.getKey();
2787 if (canceledKey.equals(mSoundNotificationKey)) {
2788 mSoundNotificationKey = null;
2789 final long identity = Binder.clearCallingIdentity();
2791 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
2792 if (player != null) {
2795 } catch (RemoteException e) {
2797 Binder.restoreCallingIdentity(identity);
2802 if (canceledKey.equals(mVibrateNotificationKey)) {
2803 mVibrateNotificationKey = null;
2804 long identity = Binder.clearCallingIdentity();
2809 Binder.restoreCallingIdentity(identity);
2814 mLights.remove(canceledKey);
2816 // Record usage stats
2818 case REASON_DELEGATE_CANCEL:
2819 case REASON_DELEGATE_CANCEL_ALL:
2820 case REASON_LISTENER_CANCEL:
2821 case REASON_LISTENER_CANCEL_ALL:
2822 mUsageStats.registerDismissedByUser(r);
2824 case REASON_NOMAN_CANCEL:
2825 case REASON_NOMAN_CANCEL_ALL:
2826 mUsageStats.registerRemovedByApp(r);
2830 mNotificationsByKey.remove(r.sbn.getKey());
2831 String groupKey = r.getGroupKey();
2832 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
2833 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
2834 mSummaryByGroupKey.remove(groupKey);
2837 // Save it for users of getHistoricalNotifications()
2838 mArchive.record(r.sbn);
2840 final long now = System.currentTimeMillis();
2841 EventLogTags.writeNotificationCanceled(canceledKey, reason,
2842 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
2846 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
2847 * and none of the {@code mustNotHaveFlags}.
2849 void cancelNotification(final int callingUid, final int callingPid,
2850 final String pkg, final String tag, final int id,
2851 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
2852 final int userId, final int reason, final ManagedServiceInfo listener) {
2853 // In enqueueNotificationInternal notifications are added by scheduling the
2854 // work on the worker handler. Hence, we also schedule the cancel on this
2855 // handler to avoid a scenario where an add notification call followed by a
2856 // remove notification call ends up in not removing the notification.
2857 mHandler.post(new Runnable() {
2860 String listenerName = listener == null ? null : listener.component.toShortString();
2861 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
2862 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
2864 synchronized (mNotificationList) {
2865 int index = indexOfNotificationLocked(pkg, tag, id, userId);
2867 NotificationRecord r = mNotificationList.get(index);
2869 // Ideally we'd do this in the caller of this method. However, that would
2870 // require the caller to also find the notification.
2871 if (reason == REASON_DELEGATE_CLICK) {
2872 mUsageStats.registerClickedByUser(r);
2875 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
2878 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
2882 mNotificationList.remove(index);
2884 cancelNotificationLocked(r, sendDelete, reason);
2885 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
2886 REASON_GROUP_SUMMARY_CANCELED);
2887 updateLightsLocked();
2895 * Determine whether the userId applies to the notification in question, either because
2896 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
2898 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
2900 // looking for USER_ALL notifications? match everything
2901 userId == UserHandle.USER_ALL
2902 // a notification sent to USER_ALL matches any query
2903 || r.getUserId() == UserHandle.USER_ALL
2904 // an exact user match
2905 || r.getUserId() == userId;
2909 * Determine whether the userId applies to the notification in question, either because
2910 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
2911 * because it matches one of the users profiles.
2913 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
2914 return notificationMatchesUserId(r, userId)
2915 || mUserProfiles.isCurrentProfile(r.getUserId());
2919 * Cancels all notifications from a given package that have all of the
2920 * {@code mustHaveFlags}.
2922 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
2923 int mustNotHaveFlags, boolean doit, int userId, int reason,
2924 ManagedServiceInfo listener) {
2925 String listenerName = listener == null ? null : listener.component.toShortString();
2926 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2927 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
2930 synchronized (mNotificationList) {
2931 final int N = mNotificationList.size();
2932 ArrayList<NotificationRecord> canceledNotifications = null;
2933 for (int i = N-1; i >= 0; --i) {
2934 NotificationRecord r = mNotificationList.get(i);
2935 if (!notificationMatchesUserId(r, userId)) {
2938 // Don't remove notifications to all, if there's no package name specified
2939 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
2942 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
2945 if ((r.getFlags() & mustNotHaveFlags) != 0) {
2948 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
2951 if (canceledNotifications == null) {
2952 canceledNotifications = new ArrayList<>();
2954 canceledNotifications.add(r);
2958 mNotificationList.remove(i);
2959 cancelNotificationLocked(r, false, reason);
2961 if (doit && canceledNotifications != null) {
2962 final int M = canceledNotifications.size();
2963 for (int i = 0; i < M; i++) {
2964 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
2965 listenerName, REASON_GROUP_SUMMARY_CANCELED);
2968 if (canceledNotifications != null) {
2969 updateLightsLocked();
2971 return canceledNotifications != null;
2975 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
2976 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
2977 String listenerName = listener == null ? null : listener.component.toShortString();
2978 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
2979 null, userId, 0, 0, reason, listenerName);
2981 ArrayList<NotificationRecord> canceledNotifications = null;
2982 final int N = mNotificationList.size();
2983 for (int i=N-1; i>=0; i--) {
2984 NotificationRecord r = mNotificationList.get(i);
2985 if (includeCurrentProfiles) {
2986 if (!notificationMatchesCurrentProfiles(r, userId)) {
2990 if (!notificationMatchesUserId(r, userId)) {
2995 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
2996 | Notification.FLAG_NO_CLEAR)) == 0) {
2997 mNotificationList.remove(i);
2998 cancelNotificationLocked(r, true, reason);
2999 // Make a note so we can cancel children later.
3000 if (canceledNotifications == null) {
3001 canceledNotifications = new ArrayList<>();
3003 canceledNotifications.add(r);
3006 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3007 for (int i = 0; i < M; i++) {
3008 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3009 listenerName, REASON_GROUP_SUMMARY_CANCELED);
3011 updateLightsLocked();
3014 // Warning: The caller is responsible for invoking updateLightsLocked().
3015 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3016 String listenerName, int reason) {
3017 Notification n = r.getNotification();
3018 if (!n.isGroupSummary()) {
3022 String pkg = r.sbn.getPackageName();
3023 int userId = r.getUserId();
3026 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3030 final int N = mNotificationList.size();
3031 for (int i = N - 1; i >= 0; i--) {
3032 NotificationRecord childR = mNotificationList.get(i);
3033 StatusBarNotification childSbn = childR.sbn;
3034 if (childR.getNotification().isGroupChild() &&
3035 childR.getGroupKey().equals(r.getGroupKey())) {
3036 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3037 childSbn.getTag(), userId, 0, 0, reason, listenerName);
3038 mNotificationList.remove(i);
3039 cancelNotificationLocked(childR, false, reason);
3044 // lock on mNotificationList
3045 void updateLightsLocked()
3047 // handle notification lights
3048 NotificationRecord ledNotification = null;
3049 while (ledNotification == null && !mLights.isEmpty()) {
3050 final String owner = mLights.get(mLights.size() - 1);
3051 ledNotification = mNotificationsByKey.get(owner);
3052 if (ledNotification == null) {
3053 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3054 mLights.remove(owner);
3058 // Don't flash while we are in a call or screen is on
3059 if (ledNotification == null || mInCall || mScreenOn) {
3060 mNotificationLight.turnOff();
3061 mStatusBar.notificationLightOff();
3063 final Notification ledno = ledNotification.sbn.getNotification();
3064 int ledARGB = ledno.ledARGB;
3065 int ledOnMS = ledno.ledOnMS;
3066 int ledOffMS = ledno.ledOffMS;
3067 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3068 ledARGB = mDefaultNotificationColor;
3069 ledOnMS = mDefaultNotificationLedOn;
3070 ledOffMS = mDefaultNotificationLedOff;
3072 if (mNotificationPulseEnabled) {
3074 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3077 // let SystemUI make an independent decision
3078 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3082 // lock on mNotificationList
3083 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3085 ArrayList<NotificationRecord> list = mNotificationList;
3086 final int len = list.size();
3087 for (int i=0; i<len; i++) {
3088 NotificationRecord r = list.get(i);
3089 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3090 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3097 // lock on mNotificationList
3098 int indexOfNotificationLocked(String key) {
3099 final int N = mNotificationList.size();
3100 for (int i = 0; i < N; i++) {
3101 if (key.equals(mNotificationList.get(i).getKey())) {
3108 private void updateNotificationPulse() {
3109 synchronized (mNotificationList) {
3110 updateLightsLocked();
3114 private static boolean isUidSystem(int uid) {
3115 final int appid = UserHandle.getAppId(uid);
3116 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3119 private static boolean isCallerSystem() {
3120 return isUidSystem(Binder.getCallingUid());
3123 private static void checkCallerIsSystem() {
3124 if (isCallerSystem()) {
3127 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3130 private static void checkCallerIsSystemOrSameApp(String pkg) {
3131 if (isCallerSystem()) {
3134 checkCallerIsSameApp(pkg);
3137 private static void checkCallerIsSameApp(String pkg) {
3138 final int uid = Binder.getCallingUid();
3140 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3141 pkg, 0, UserHandle.getCallingUserId());
3143 throw new SecurityException("Unknown package " + pkg);
3145 if (!UserHandle.isSameApp(ai.uid, uid)) {
3146 throw new SecurityException("Calling uid " + uid + " gave package"
3147 + pkg + " which is owned by uid " + ai.uid);
3149 } catch (RemoteException re) {
3150 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3154 private static String callStateToString(int state) {
3156 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3157 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3158 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3159 default: return "CALL_STATE_UNKNOWN_" + state;
3163 private void listenForCallState() {
3164 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3166 public void onCallStateChanged(int state, String incomingNumber) {
3167 if (mCallState == state) return;
3168 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3171 }, PhoneStateListener.LISTEN_CALL_STATE);
3175 * Generates a NotificationRankingUpdate from 'sbns', considering only
3176 * notifications visible to the given listener.
3178 * <p>Caller must hold a lock on mNotificationList.</p>
3180 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3181 int speedBumpIndex = -1;
3182 final int N = mNotificationList.size();
3183 ArrayList<String> keys = new ArrayList<String>(N);
3184 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3185 Bundle visibilityOverrides = new Bundle();
3186 for (int i = 0; i < N; i++) {
3187 NotificationRecord record = mNotificationList.get(i);
3188 if (!isVisibleToListener(record.sbn, info)) {
3191 keys.add(record.sbn.getKey());
3192 if (record.isIntercepted()) {
3193 interceptedKeys.add(record.sbn.getKey());
3195 if (record.getPackageVisibilityOverride()
3196 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3197 visibilityOverrides.putInt(record.sbn.getKey(),
3198 record.getPackageVisibilityOverride());
3200 // Find first min-prio notification for speedbump placement.
3201 if (speedBumpIndex == -1 &&
3202 // Intrusiveness trumps priority, hence ignore intrusives.
3203 !record.isRecentlyIntrusive() &&
3204 // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
3205 // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
3206 // (or lower as a safeguard) is sufficient to find the speedbump index.
3207 // We'll have to revisit this when more package priority buckets are introduced.
3208 record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
3209 record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
3210 speedBumpIndex = keys.size() - 1;
3213 String[] keysAr = keys.toArray(new String[keys.size()]);
3214 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3215 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3219 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3220 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3223 // TODO: remove this for older listeners.
3227 public class NotificationListeners extends ManagedServices {
3229 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3230 private boolean mNotificationGroupsDesired;
3232 public NotificationListeners() {
3233 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3237 protected Config getConfig() {
3238 Config c = new Config();
3239 c.caption = "notification listener";
3240 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3241 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3242 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3243 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3244 c.clientLabel = R.string.notification_listener_binding_label;
3249 protected IInterface asInterface(IBinder binder) {
3250 return INotificationListener.Stub.asInterface(binder);
3254 public void onServiceAdded(ManagedServiceInfo info) {
3255 final INotificationListener listener = (INotificationListener) info.service;
3256 final NotificationRankingUpdate update;
3257 synchronized (mNotificationList) {
3258 updateNotificationGroupsDesiredLocked();
3259 update = makeRankingUpdateLocked(info);
3262 listener.onListenerConnected(update);
3263 } catch (RemoteException e) {
3269 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3270 if (mListenersDisablingEffects.remove(removed)) {
3271 updateListenerHintsLocked();
3272 updateEffectsSuppressorLocked();
3274 mLightTrimListeners.remove(removed);
3275 updateNotificationGroupsDesiredLocked();
3278 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3279 if (trim == TRIM_LIGHT) {
3280 mLightTrimListeners.add(info);
3282 mLightTrimListeners.remove(info);
3286 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3287 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3292 * asynchronously notify all listeners about a new notification
3295 * Also takes care of removing a notification that has been visible to a listener before,
3296 * but isn't anymore.
3298 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
3299 // Lazily initialized snapshots of the notification.
3300 StatusBarNotification sbnClone = null;
3301 StatusBarNotification sbnCloneLight = null;
3303 for (final ManagedServiceInfo info : mServices) {
3304 boolean sbnVisible = isVisibleToListener(sbn, info);
3305 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3306 // This notification hasn't been and still isn't visible -> ignore.
3307 if (!oldSbnVisible && !sbnVisible) {
3310 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3312 // This notification became invisible -> remove the old one.
3313 if (oldSbnVisible && !sbnVisible) {
3314 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
3315 mHandler.post(new Runnable() {
3318 notifyRemoved(info, oldSbnLightClone, update);
3324 final int trim = mListeners.getOnNotificationPostedTrim(info);
3326 if (trim == TRIM_LIGHT && sbnCloneLight == null) {
3327 sbnCloneLight = sbn.cloneLight();
3328 } else if (trim == TRIM_FULL && sbnClone == null) {
3329 sbnClone = sbn.clone();
3331 final StatusBarNotification sbnToPost =
3332 (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
3334 mHandler.post(new Runnable() {
3337 notifyPosted(info, sbnToPost, update);
3344 * asynchronously notify all listeners about a removed notification
3346 public void notifyRemovedLocked(StatusBarNotification sbn) {
3347 // make a copy in case changes are made to the underlying Notification object
3348 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
3350 final StatusBarNotification sbnLight = sbn.cloneLight();
3351 for (final ManagedServiceInfo info : mServices) {
3352 if (!isVisibleToListener(sbn, info)) {
3355 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
3356 mHandler.post(new Runnable() {
3359 notifyRemoved(info, sbnLight, update);
3366 * asynchronously notify all listeners about a reordering of notifications
3368 public void notifyRankingUpdateLocked() {
3369 for (final ManagedServiceInfo serviceInfo : mServices) {
3370 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3373 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
3374 mHandler.post(new Runnable() {
3377 notifyRankingUpdate(serviceInfo, update);
3383 public void notifyListenerHintsChangedLocked(final int hints) {
3384 for (final ManagedServiceInfo serviceInfo : mServices) {
3385 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3388 mHandler.post(new Runnable() {
3391 notifyListenerHintsChanged(serviceInfo, hints);
3397 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
3398 for (final ManagedServiceInfo serviceInfo : mServices) {
3399 if (!serviceInfo.isEnabledForCurrentProfiles()) {
3402 mHandler.post(new Runnable() {
3405 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
3411 private void notifyPosted(final ManagedServiceInfo info,
3412 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
3413 final INotificationListener listener = (INotificationListener)info.service;
3414 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3416 listener.onNotificationPosted(sbnHolder, rankingUpdate);
3417 } catch (RemoteException ex) {
3418 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
3422 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
3423 NotificationRankingUpdate rankingUpdate) {
3424 if (!info.enabledAndUserMatches(sbn.getUserId())) {
3427 final INotificationListener listener = (INotificationListener) info.service;
3428 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3430 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
3431 } catch (RemoteException ex) {
3432 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
3436 private void notifyRankingUpdate(ManagedServiceInfo info,
3437 NotificationRankingUpdate rankingUpdate) {
3438 final INotificationListener listener = (INotificationListener) info.service;
3440 listener.onNotificationRankingUpdate(rankingUpdate);
3441 } catch (RemoteException ex) {
3442 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
3446 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
3447 final INotificationListener listener = (INotificationListener) info.service;
3449 listener.onListenerHintsChanged(hints);
3450 } catch (RemoteException ex) {
3451 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
3455 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
3456 int interruptionFilter) {
3457 final INotificationListener listener = (INotificationListener) info.service;
3459 listener.onInterruptionFilterChanged(interruptionFilter);
3460 } catch (RemoteException ex) {
3461 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
3465 private boolean isListenerPackage(String packageName) {
3466 if (packageName == null) {
3469 // TODO: clean up locking object later
3470 synchronized (mNotificationList) {
3471 for (final ManagedServiceInfo serviceInfo : mServices) {
3472 if (packageName.equals(serviceInfo.component.getPackageName())) {
3481 * Returns whether any of the currently registered listeners wants to receive notification
3484 * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
3486 public boolean notificationGroupsDesired() {
3487 return mNotificationGroupsDesired;
3490 private void updateNotificationGroupsDesiredLocked() {
3491 mNotificationGroupsDesired = true;
3492 // No listeners, no groups.
3493 if (mServices.isEmpty()) {
3494 mNotificationGroupsDesired = false;
3497 // One listener: Check whether it's SysUI.
3498 if (mServices.size() == 1 &&
3499 mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
3500 mNotificationGroupsDesired = false;
3506 public static final class DumpFilter {
3507 public boolean filtered = false;
3508 public String pkgFilter;
3511 public boolean stats;
3512 public boolean redact = true;
3514 public static DumpFilter parseFromArguments(String[] args) {
3515 final DumpFilter filter = new DumpFilter();
3516 for (int ai = 0; ai < args.length; ai++) {
3517 final String a = args[ai];
3518 if ("--noredact".equals(a) || "--reveal".equals(a)) {
3519 filter.redact = false;
3520 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
3521 if (ai < args.length-1) {
3523 filter.pkgFilter = args[ai].trim().toLowerCase();
3524 if (filter.pkgFilter.isEmpty()) {
3525 filter.pkgFilter = null;
3527 filter.filtered = true;
3530 } else if ("--zen".equals(a) || "zen".equals(a)) {
3531 filter.filtered = true;
3533 } else if ("--stats".equals(a)) {
3534 filter.stats = true;
3535 if (ai < args.length-1) {
3537 filter.since = Long.valueOf(args[ai]);
3546 public boolean matches(StatusBarNotification sbn) {
3547 if (!filtered) return true;
3548 return zen ? true : sbn != null
3549 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
3552 public boolean matches(ComponentName component) {
3553 if (!filtered) return true;
3554 return zen ? true : component != null && matches(component.getPackageName());
3557 public boolean matches(String pkg) {
3558 if (!filtered) return true;
3559 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
3563 public String toString() {
3564 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
3569 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
3570 * binder without sending large amounts of data over a oneway transaction.
3572 private static final class StatusBarNotificationHolder
3573 extends IStatusBarNotificationHolder.Stub {
3574 private StatusBarNotification mValue;
3576 public StatusBarNotificationHolder(StatusBarNotification value) {
3580 /** Get the held value and clear it. This function should only be called once per holder */
3582 public StatusBarNotification get() {
3583 StatusBarNotification value = mValue;
3589 private final class PolicyAccess {
3590 private static final String SEPARATOR = ":";
3591 private final String[] PERM = {
3592 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
3595 public boolean isPackageGranted(String pkg) {
3596 return pkg != null && getGrantedPackages().contains(pkg);
3599 public void put(String pkg, boolean granted) {
3600 if (pkg == null) return;
3601 final ArraySet<String> pkgs = getGrantedPackages();
3604 changed = pkgs.add(pkg);
3606 changed = pkgs.remove(pkg);
3608 if (!changed) return;
3609 final String setting = TextUtils.join(SEPARATOR, pkgs);
3610 final int currentUser = ActivityManager.getCurrentUser();
3611 Settings.Secure.putStringForUser(getContext().getContentResolver(),
3612 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3615 getContext().sendBroadcastAsUser(new Intent(NotificationManager
3616 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
3618 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
3621 public ArraySet<String> getGrantedPackages() {
3622 final ArraySet<String> pkgs = new ArraySet<>();
3624 long identity = Binder.clearCallingIdentity();
3626 final String setting = Settings.Secure.getStringForUser(
3627 getContext().getContentResolver(),
3628 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
3629 ActivityManager.getCurrentUser());
3630 if (setting != null) {
3631 final String[] tokens = setting.split(SEPARATOR);
3632 for (int i = 0; i < tokens.length; i++) {
3633 String token = tokens[i];
3634 if (token != null) {
3637 if (TextUtils.isEmpty(token)) {
3644 Binder.restoreCallingIdentity(identity);
3649 public String[] getRequestingPackages() throws RemoteException {
3650 final ParceledListSlice list = AppGlobals.getPackageManager()
3651 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
3652 ActivityManager.getCurrentUser());
3653 final List<PackageInfo> pkgs = list.getList();
3654 if (pkgs == null || pkgs.isEmpty()) return new String[0];
3655 final int N = pkgs.size();
3656 final String[] rt = new String[N];
3657 for (int i = 0; i < N; i++) {
3658 rt[i] = pkgs.get(i).packageName;