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.NotificationRankerService.REASON_APP_CANCEL;
20 import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
21 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
22 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
23 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
24 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
25 import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
26 import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
27 import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
28 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
29 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
30 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
31 import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
32 import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
33 import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
34 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
35 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
36 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
37 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
38 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
39 import static android.service.notification.NotificationListenerService.TRIM_FULL;
40 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
41 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
42 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
44 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
46 import android.Manifest;
47 import android.annotation.Nullable;
48 import android.app.ActivityManager;
49 import android.app.ActivityManagerInternal;
50 import android.app.ActivityManagerNative;
51 import android.app.AppGlobals;
52 import android.app.AppOpsManager;
53 import android.app.AutomaticZenRule;
54 import android.app.IActivityManager;
55 import android.app.INotificationManager;
56 import android.app.ITransientNotification;
57 import android.app.Notification;
58 import android.app.NotificationManager;
59 import android.app.NotificationManager.Policy;
60 import android.app.PendingIntent;
61 import android.app.RemoteInput;
62 import android.app.StatusBarManager;
63 import android.app.backup.BackupManager;
64 import android.app.usage.UsageEvents;
65 import android.app.usage.UsageStatsManagerInternal;
66 import android.content.BroadcastReceiver;
67 import android.content.ComponentName;
68 import android.content.ContentResolver;
69 import android.content.Context;
70 import android.content.Intent;
71 import android.content.IntentFilter;
72 import android.content.pm.ApplicationInfo;
73 import android.content.pm.IPackageManager;
74 import android.content.pm.PackageInfo;
75 import android.content.pm.PackageManager;
76 import android.content.pm.PackageManager.NameNotFoundException;
77 import android.content.pm.ParceledListSlice;
78 import android.content.pm.UserInfo;
79 import android.content.res.Resources;
80 import android.database.ContentObserver;
81 import android.media.AudioAttributes;
82 import android.media.AudioManager;
83 import android.media.AudioManagerInternal;
84 import android.media.AudioSystem;
85 import android.media.IRingtonePlayer;
86 import android.net.Uri;
87 import android.os.Binder;
88 import android.os.Bundle;
89 import android.os.Environment;
90 import android.os.Handler;
91 import android.os.HandlerThread;
92 import android.os.IBinder;
93 import android.os.IInterface;
94 import android.os.Looper;
95 import android.os.Message;
96 import android.os.Parcelable;
97 import android.os.Process;
98 import android.os.RemoteException;
99 import android.os.SystemClock;
100 import android.os.SystemProperties;
101 import android.os.UserHandle;
102 import android.os.UserManager;
103 import android.os.Vibrator;
104 import android.provider.Settings;
105 import android.service.notification.Adjustment;
106 import android.service.notification.Condition;
107 import android.service.notification.IConditionProvider;
108 import android.service.notification.INotificationListener;
109 import android.service.notification.IStatusBarNotificationHolder;
110 import android.service.notification.NotificationRankerService;
111 import android.service.notification.NotificationListenerService;
112 import android.service.notification.NotificationRankingUpdate;
113 import android.service.notification.StatusBarNotification;
114 import android.service.notification.ZenModeConfig;
115 import android.telephony.PhoneStateListener;
116 import android.telephony.TelephonyManager;
117 import android.text.TextUtils;
118 import android.util.ArrayMap;
119 import android.util.ArraySet;
120 import android.util.AtomicFile;
121 import android.util.Log;
122 import android.util.Slog;
123 import android.util.SparseArray;
124 import android.util.Xml;
125 import android.view.accessibility.AccessibilityEvent;
126 import android.view.accessibility.AccessibilityManager;
127 import android.widget.Toast;
129 import com.android.internal.R;
130 import com.android.internal.annotations.VisibleForTesting;
131 import com.android.internal.statusbar.NotificationVisibility;
132 import com.android.internal.util.FastXmlSerializer;
133 import com.android.internal.util.Preconditions;
134 import com.android.server.DeviceIdleController;
135 import com.android.server.EventLogTags;
136 import com.android.server.LocalServices;
137 import com.android.server.SystemService;
138 import com.android.server.lights.Light;
139 import com.android.server.lights.LightsManager;
140 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
141 import com.android.server.statusbar.StatusBarManagerInternal;
142 import com.android.server.vr.VrManagerInternal;
143 import com.android.server.notification.ManagedServices.UserProfiles;
145 import libcore.io.IoUtils;
147 import org.json.JSONException;
148 import org.json.JSONObject;
149 import org.xmlpull.v1.XmlPullParser;
150 import org.xmlpull.v1.XmlPullParserException;
151 import org.xmlpull.v1.XmlSerializer;
153 import java.io.ByteArrayInputStream;
154 import java.io.ByteArrayOutputStream;
156 import java.io.FileDescriptor;
157 import java.io.FileInputStream;
158 import java.io.FileNotFoundException;
159 import java.io.FileOutputStream;
160 import java.io.IOException;
161 import java.io.InputStream;
162 import java.io.OutputStream;
163 import java.io.PrintWriter;
164 import java.nio.charset.StandardCharsets;
165 import java.util.ArrayDeque;
166 import java.util.ArrayList;
167 import java.util.Arrays;
168 import java.util.Iterator;
169 import java.util.List;
170 import java.util.Map;
171 import java.util.Map.Entry;
172 import java.util.Set;
173 import java.util.concurrent.TimeUnit;
176 public class NotificationManagerService extends SystemService {
177 static final String TAG = "NotificationService";
178 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
179 public static final boolean ENABLE_CHILD_NOTIFICATIONS
180 = SystemProperties.getBoolean("debug.child_notifs", true);
182 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
183 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 50f;
186 static final int MESSAGE_TIMEOUT = 2;
187 static final int MESSAGE_SAVE_POLICY_FILE = 3;
188 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
189 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
190 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
192 // ranking thread messages
193 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
194 private static final int MESSAGE_RANKING_SORT = 1001;
196 static final int LONG_DELAY = 3500; // 3.5 seconds
197 static final int SHORT_DELAY = 2000; // 2 seconds
199 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
201 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
203 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
205 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
206 static final boolean ENABLE_BLOCKED_TOASTS = true;
208 // When #matchesCallFilter is called from the ringer, wait at most
209 // 3s to resolve the contacts. This timeout is required since
210 // ContactsProvider might take a long time to start up.
212 // Return STARRED_CONTACT when the timeout is hit in order to avoid
213 // missed calls in ZEN mode "Important".
214 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
215 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
216 ValidateNotificationPeople.STARRED_CONTACT;
218 /** notification_enqueue status value for a newly enqueued notification. */
219 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
221 /** notification_enqueue status value for an existing notification. */
222 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
224 /** notification_enqueue status value for an ignored notification. */
225 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
226 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
227 private String mRankerServicePackageName;
229 private IActivityManager mAm;
230 AudioManager mAudioManager;
231 AudioManagerInternal mAudioManagerInternal;
232 @Nullable StatusBarManagerInternal mStatusBar;
234 private VrManagerInternal mVrManagerInternal;
236 final IBinder mForegroundToken = new Binder();
237 private Handler mHandler;
238 private final HandlerThread mRankingThread = new HandlerThread("ranker",
239 Process.THREAD_PRIORITY_BACKGROUND);
241 private Light mNotificationLight;
242 Light mAttentionLight;
243 private int mDefaultNotificationColor;
244 private int mDefaultNotificationLedOn;
246 private int mDefaultNotificationLedOff;
247 private long[] mDefaultVibrationPattern;
249 private long[] mFallbackVibrationPattern;
250 private boolean mUseAttentionLight;
251 boolean mSystemReady;
253 private boolean mDisableNotificationEffects;
254 private int mCallState;
255 private String mSoundNotificationKey;
256 private String mVibrateNotificationKey;
258 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
259 new SparseArray<ArraySet<ManagedServiceInfo>>();
260 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
261 private int mListenerHints; // right now, all hints are global
262 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
264 // for enabling and disabling notification pulse behavior
265 private boolean mScreenOn = true;
266 private boolean mInCall = false;
267 private boolean mNotificationPulseEnabled;
269 // used as a mutex for access to all active notifications & listeners
270 final ArrayList<NotificationRecord> mNotificationList =
271 new ArrayList<NotificationRecord>();
272 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
273 new ArrayMap<String, NotificationRecord>();
274 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
275 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
276 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
277 final PolicyAccess mPolicyAccess = new PolicyAccess();
279 // The last key in this list owns the hardware.
280 ArrayList<String> mLights = new ArrayList<>();
282 private AppOpsManager mAppOps;
283 private UsageStatsManagerInternal mAppUsageStats;
285 private Archive mArchive;
287 // Persistent storage for notification policy
288 private AtomicFile mPolicyFile;
290 private static final int DB_VERSION = 1;
292 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
293 private static final String ATTR_VERSION = "version";
295 private RankingHelper mRankingHelper;
297 private final UserProfiles mUserProfiles = new UserProfiles();
298 private NotificationListeners mListeners;
299 private NotificationRankers mRankerServices;
300 private ConditionProviders mConditionProviders;
301 private NotificationUsageStats mUsageStats;
303 private static final int MY_UID = Process.myUid();
304 private static final int MY_PID = Process.myPid();
305 private RankingHandler mRankingHandler;
306 private long mLastOverRateLogTime;
307 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
308 private String mSystemNotificationSound;
310 private static class Archive {
311 final int mBufferSize;
312 final ArrayDeque<StatusBarNotification> mBuffer;
314 public Archive(int size) {
316 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
319 public String toString() {
320 final StringBuilder sb = new StringBuilder();
321 final int N = mBuffer.size();
322 sb.append("Archive (");
324 sb.append(" notification");
325 sb.append((N==1)?")":"s)");
326 return sb.toString();
329 public void record(StatusBarNotification nr) {
330 if (mBuffer.size() == mBufferSize) {
331 mBuffer.removeFirst();
334 // We don't want to store the heavy bits of the notification in the archive,
335 // but other clients in the system process might be using the object, so we
336 // store a (lightened) copy.
337 mBuffer.addLast(nr.cloneLight());
340 public Iterator<StatusBarNotification> descendingIterator() {
341 return mBuffer.descendingIterator();
344 public StatusBarNotification[] getArray(int count) {
345 if (count == 0) count = mBufferSize;
346 final StatusBarNotification[] a
347 = new StatusBarNotification[Math.min(count, mBuffer.size())];
348 Iterator<StatusBarNotification> iter = descendingIterator();
350 while (iter.hasNext() && i < count) {
351 a[i++] = iter.next();
358 private void readPolicyXml(InputStream stream, boolean forRestore)
359 throws XmlPullParserException, NumberFormatException, IOException {
360 final XmlPullParser parser = Xml.newPullParser();
361 parser.setInput(stream, StandardCharsets.UTF_8.name());
363 while (parser.next() != END_DOCUMENT) {
364 mZenModeHelper.readXml(parser, forRestore);
365 mRankingHelper.readXml(parser, forRestore);
369 private void loadPolicyFile() {
370 if (DBG) Slog.d(TAG, "loadPolicyFile");
371 synchronized(mPolicyFile) {
373 FileInputStream infile = null;
375 infile = mPolicyFile.openRead();
376 readPolicyXml(infile, false /*forRestore*/);
377 } catch (FileNotFoundException e) {
379 } catch (IOException e) {
380 Log.wtf(TAG, "Unable to read notification policy", e);
381 } catch (NumberFormatException e) {
382 Log.wtf(TAG, "Unable to parse notification policy", e);
383 } catch (XmlPullParserException e) {
384 Log.wtf(TAG, "Unable to parse notification policy", e);
386 IoUtils.closeQuietly(infile);
391 public void savePolicyFile() {
392 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
393 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
396 private void handleSavePolicyFile() {
397 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
398 synchronized (mPolicyFile) {
399 final FileOutputStream stream;
401 stream = mPolicyFile.startWrite();
402 } catch (IOException e) {
403 Slog.w(TAG, "Failed to save policy file", e);
408 writePolicyXml(stream, false /*forBackup*/);
409 mPolicyFile.finishWrite(stream);
410 } catch (IOException e) {
411 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
412 mPolicyFile.failWrite(stream);
415 BackupManager.dataChanged(getContext().getPackageName());
418 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
419 final XmlSerializer out = new FastXmlSerializer();
420 out.setOutput(stream, StandardCharsets.UTF_8.name());
421 out.startDocument(null, true);
422 out.startTag(null, TAG_NOTIFICATION_POLICY);
423 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
424 mZenModeHelper.writeXml(out, forBackup);
425 mRankingHelper.writeXml(out, forBackup);
426 out.endTag(null, TAG_NOTIFICATION_POLICY);
430 /** Use this when you actually want to post a notification or toast.
432 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
434 private boolean noteNotificationOp(String pkg, int uid) {
435 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
436 != AppOpsManager.MODE_ALLOWED) {
437 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
443 /** Use this to check if a package can post a notification or toast. */
444 private boolean checkNotificationOp(String pkg, int uid) {
445 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
446 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
449 private static final class ToastRecord
453 final ITransientNotification callback;
456 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
460 this.callback = callback;
461 this.duration = duration;
464 void update(int duration) {
465 this.duration = duration;
468 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
469 if (filter != null && !filter.matches(pkg)) return;
470 pw.println(prefix + this);
474 public final String toString()
476 return "ToastRecord{"
477 + Integer.toHexString(System.identityHashCode(this))
479 + " callback=" + callback
480 + " duration=" + duration;
484 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
487 public void onSetDisabled(int status) {
488 synchronized (mNotificationList) {
489 mDisableNotificationEffects =
490 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
491 if (disableNotificationEffects(null) != null) {
492 // cancel whatever's going on
493 long identity = Binder.clearCallingIdentity();
495 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
496 if (player != null) {
499 } catch (RemoteException e) {
501 Binder.restoreCallingIdentity(identity);
504 identity = Binder.clearCallingIdentity();
508 Binder.restoreCallingIdentity(identity);
515 public void onClearAll(int callingUid, int callingPid, int userId) {
516 synchronized (mNotificationList) {
517 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
518 /*includeCurrentProfiles*/ true);
523 public void onNotificationClick(int callingUid, int callingPid, String key) {
524 synchronized (mNotificationList) {
525 NotificationRecord r = mNotificationsByKey.get(key);
527 Log.w(TAG, "No notification with key: " + key);
530 final long now = System.currentTimeMillis();
531 EventLogTags.writeNotificationClicked(key,
532 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
534 StatusBarNotification sbn = r.sbn;
535 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
536 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
537 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
538 REASON_DELEGATE_CLICK, null);
543 public void onNotificationActionClick(int callingUid, int callingPid, String key,
545 synchronized (mNotificationList) {
546 NotificationRecord r = mNotificationsByKey.get(key);
548 Log.w(TAG, "No notification with key: " + key);
551 final long now = System.currentTimeMillis();
552 EventLogTags.writeNotificationActionClicked(key, actionIndex,
553 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
554 // TODO: Log action click via UsageStats.
559 public void onNotificationClear(int callingUid, int callingPid,
560 String pkg, String tag, int id, int userId) {
561 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
562 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
563 true, userId, REASON_DELEGATE_CANCEL, null);
567 public void onPanelRevealed(boolean clearEffects, int items) {
568 EventLogTags.writeNotificationPanelRevealed(items);
575 public void onPanelHidden() {
576 EventLogTags.writeNotificationPanelHidden();
580 public void clearEffects() {
581 synchronized (mNotificationList) {
582 if (DBG) Slog.d(TAG, "clearEffects");
584 clearVibrateLocked();
590 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
591 int uid, int initialPid, String message, int userId) {
592 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
593 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
594 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
595 REASON_DELEGATE_ERROR, null);
596 long ident = Binder.clearCallingIdentity();
598 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
599 "Bad notification posted from package " + pkg
601 } catch (RemoteException e) {
603 Binder.restoreCallingIdentity(ident);
607 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
608 NotificationVisibility[] noLongerVisibleKeys) {
609 synchronized (mNotificationList) {
610 for (NotificationVisibility nv : newlyVisibleKeys) {
611 NotificationRecord r = mNotificationsByKey.get(nv.key);
612 if (r == null) continue;
613 r.setVisibility(true, nv.rank);
616 // Note that we might receive this event after notifications
617 // have already left the system, e.g. after dismissing from the
618 // shade. Hence not finding notifications in
619 // mNotificationsByKey is not an exceptional condition.
620 for (NotificationVisibility nv : noLongerVisibleKeys) {
621 NotificationRecord r = mNotificationsByKey.get(nv.key);
622 if (r == null) continue;
623 r.setVisibility(false, nv.rank);
630 public void onNotificationExpansionChanged(String key,
631 boolean userAction, boolean expanded) {
632 synchronized (mNotificationList) {
633 NotificationRecord r = mNotificationsByKey.get(key);
635 r.stats.onExpansionChanged(userAction, expanded);
636 final long now = System.currentTimeMillis();
637 EventLogTags.writeNotificationExpansion(key,
638 userAction ? 1 : 0, expanded ? 1 : 0,
639 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
645 private void clearSoundLocked() {
646 mSoundNotificationKey = null;
647 long identity = Binder.clearCallingIdentity();
649 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
650 if (player != null) {
653 } catch (RemoteException e) {
655 Binder.restoreCallingIdentity(identity);
659 private void clearVibrateLocked() {
660 mVibrateNotificationKey = null;
661 long identity = Binder.clearCallingIdentity();
665 Binder.restoreCallingIdentity(identity);
669 private void clearLightsLocked() {
672 updateLightsLocked();
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;
687 int reason = REASON_PACKAGE_CHANGED;
689 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
690 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
691 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
692 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
693 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
694 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
695 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
696 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
697 UserHandle.USER_ALL);
698 String pkgList[] = null;
699 boolean queryReplace = queryRemove &&
700 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
701 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
702 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
703 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
704 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
705 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
706 reason = REASON_PACKAGE_SUSPENDED;
707 } else if (queryRestart) {
708 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
710 Uri uri = intent.getData();
714 String pkgName = uri.getSchemeSpecificPart();
715 if (pkgName == null) {
718 if (packageChanged) {
719 // We cancel notifications for packages which have just been disabled
721 final IPackageManager pm = AppGlobals.getPackageManager();
722 final int enabled = pm.getApplicationEnabledSetting(pkgName,
723 changeUserId != UserHandle.USER_ALL ? changeUserId :
724 UserHandle.USER_SYSTEM);
725 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
726 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
727 cancelNotifications = false;
729 } catch (IllegalArgumentException e) {
730 // Package doesn't exist; probably racing with uninstall.
731 // cancelNotifications is already true, so nothing to do here.
733 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
735 } catch (RemoteException e) {
736 // Failed to talk to PackageManagerService Should never happen!
739 pkgList = new String[]{pkgName};
742 if (pkgList != null && (pkgList.length > 0)) {
743 for (String pkgName : pkgList) {
744 if (cancelNotifications) {
745 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
746 changeUserId, reason, null);
750 mListeners.onPackagesChanged(queryReplace, pkgList);
751 mRankerServices.onPackagesChanged(queryReplace, pkgList);
752 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
753 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
758 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
760 public void onReceive(Context context, Intent intent) {
761 String action = intent.getAction();
763 if (action.equals(Intent.ACTION_SCREEN_ON)) {
764 // Keep track of screen on/off state, but do not turn off the notification light
765 // until user passes through the lock screen or views the notification.
767 updateNotificationPulse();
768 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
770 updateNotificationPulse();
771 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
772 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
773 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
774 updateNotificationPulse();
775 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
776 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
777 if (userHandle >= 0) {
778 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
779 REASON_USER_STOPPED, null);
781 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
782 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
783 if (userHandle >= 0) {
784 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
785 REASON_PROFILE_TURNED_OFF, null);
787 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
788 // turn off LED when user passes through lock screen
789 mNotificationLight.turnOff();
790 if (mStatusBar != null) {
791 mStatusBar.notificationLightOff();
793 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
794 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
795 // reload per-user settings
796 mSettingsObserver.update(null);
797 mUserProfiles.updateCache(context);
798 // Refresh managed services
799 mConditionProviders.onUserSwitched(user);
800 mListeners.onUserSwitched(user);
801 mRankerServices.onUserSwitched(user);
802 mZenModeHelper.onUserSwitched(user);
803 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
804 mUserProfiles.updateCache(context);
805 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
806 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
807 mZenModeHelper.onUserRemoved(user);
808 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
809 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
810 mConditionProviders.onUserUnlocked(user);
811 mListeners.onUserUnlocked(user);
812 mRankerServices.onUserUnlocked(user);
813 mZenModeHelper.onUserUnlocked(user);
818 private final class SettingsObserver extends ContentObserver {
819 private final Uri NOTIFICATION_LIGHT_PULSE_URI
820 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
821 private final Uri NOTIFICATION_SOUND_URI
822 = Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND);
823 private final Uri NOTIFICATION_RATE_LIMIT_URI
824 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
826 SettingsObserver(Handler handler) {
831 ContentResolver resolver = getContext().getContentResolver();
832 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
833 false, this, UserHandle.USER_ALL);
834 resolver.registerContentObserver(NOTIFICATION_SOUND_URI,
835 false, this, UserHandle.USER_ALL);
836 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
837 false, this, UserHandle.USER_ALL);
841 @Override public void onChange(boolean selfChange, Uri uri) {
845 public void update(Uri uri) {
846 ContentResolver resolver = getContext().getContentResolver();
847 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
848 boolean pulseEnabled = Settings.System.getInt(resolver,
849 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
850 if (mNotificationPulseEnabled != pulseEnabled) {
851 mNotificationPulseEnabled = pulseEnabled;
852 updateNotificationPulse();
855 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
856 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
857 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
859 if (uri == null || NOTIFICATION_SOUND_URI.equals(uri)) {
860 mSystemNotificationSound = Settings.System.getString(resolver,
861 Settings.System.NOTIFICATION_SOUND);
866 private SettingsObserver mSettingsObserver;
867 private ZenModeHelper mZenModeHelper;
869 private final Runnable mBuzzBeepBlinked = new Runnable() {
872 if (mStatusBar != null) {
873 mStatusBar.buzzBeepBlinked();
878 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
879 int[] ar = r.getIntArray(resid);
883 final int len = ar.length > maxlen ? maxlen : ar.length;
884 long[] out = new long[len];
885 for (int i=0; i<len; i++) {
891 public NotificationManagerService(Context context) {
896 void setAudioManager(AudioManager audioMananger) {
897 mAudioManager = audioMananger;
901 void setVibrator(Vibrator vibrator) {
902 mVibrator = vibrator;
906 void setSystemReady(boolean systemReady) {
907 mSystemReady = systemReady;
911 void setHandler(Handler handler) {
916 void setSystemNotificationSound(String systemNotificationSound) {
917 mSystemNotificationSound = systemNotificationSound;
921 public void onStart() {
922 Resources resources = getContext().getResources();
924 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
925 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
926 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
928 mAm = ActivityManagerNative.getDefault();
929 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
930 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
931 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
933 // This is the package that contains the AOSP framework update.
934 mRankerServicePackageName = getContext().getPackageManager()
935 .getServicesSystemSharedLibraryPackageName();
937 mHandler = new WorkerHandler();
938 mRankingThread.start();
939 String[] extractorNames;
941 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
942 } catch (Resources.NotFoundException e) {
943 extractorNames = new String[0];
945 mUsageStats = new NotificationUsageStats(getContext());
946 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
947 mRankingHelper = new RankingHelper(getContext(),
951 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
952 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
953 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
955 public void onConfigChanged() {
960 void onZenModeChanged() {
961 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
962 getContext().sendBroadcastAsUser(
963 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
964 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
965 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
966 synchronized(mNotificationList) {
967 updateInterruptionFilterLocked();
972 void onPolicyChanged() {
973 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
976 final File systemDir = new File(Environment.getDataDirectory(), "system");
977 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
981 // This is a MangedServices object that keeps track of the listeners.
982 mListeners = new NotificationListeners();
984 // This is a MangedServices object that keeps track of the ranker.
985 mRankerServices = new NotificationRankers();
986 // Find the updatable ranker and register it.
987 mRankerServices.registerRanker();
989 mStatusBar = getLocalService(StatusBarManagerInternal.class);
990 if (mStatusBar != null) {
991 mStatusBar.setNotificationDelegate(mNotificationDelegate);
994 final LightsManager lights = getLocalService(LightsManager.class);
995 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
996 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
998 mDefaultNotificationColor = resources.getColor(
999 R.color.config_defaultNotificationColor);
1000 mDefaultNotificationLedOn = resources.getInteger(
1001 R.integer.config_defaultNotificationLedOn);
1002 mDefaultNotificationLedOff = resources.getInteger(
1003 R.integer.config_defaultNotificationLedOff);
1005 mDefaultVibrationPattern = getLongArray(resources,
1006 R.array.config_defaultNotificationVibePattern,
1007 VIBRATE_PATTERN_MAXLEN,
1008 DEFAULT_VIBRATE_PATTERN);
1010 mFallbackVibrationPattern = getLongArray(resources,
1011 R.array.config_notificationFallbackVibePattern,
1012 VIBRATE_PATTERN_MAXLEN,
1013 DEFAULT_VIBRATE_PATTERN);
1015 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1017 // Don't start allowing notifications until the setup wizard has run once.
1018 // After that, including subsequent boots, init with notifications turned on.
1019 // This works on the first boot because the setup wizard will toggle this
1020 // flag at least once and we'll go back to 0 after that.
1021 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1022 Settings.Global.DEVICE_PROVISIONED, 0)) {
1023 mDisableNotificationEffects = true;
1025 mZenModeHelper.initZenMode();
1026 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1028 mUserProfiles.updateCache(getContext());
1029 listenForCallState();
1031 // register for various Intents
1032 IntentFilter filter = new IntentFilter();
1033 filter.addAction(Intent.ACTION_SCREEN_ON);
1034 filter.addAction(Intent.ACTION_SCREEN_OFF);
1035 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1036 filter.addAction(Intent.ACTION_USER_PRESENT);
1037 filter.addAction(Intent.ACTION_USER_STOPPED);
1038 filter.addAction(Intent.ACTION_USER_SWITCHED);
1039 filter.addAction(Intent.ACTION_USER_ADDED);
1040 filter.addAction(Intent.ACTION_USER_REMOVED);
1041 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1042 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1043 getContext().registerReceiver(mIntentReceiver, filter);
1045 IntentFilter pkgFilter = new IntentFilter();
1046 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1047 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1048 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1049 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1050 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1051 pkgFilter.addDataScheme("package");
1052 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1055 IntentFilter suspendedPkgFilter = new IntentFilter();
1056 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1057 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1058 suspendedPkgFilter, null, null);
1060 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1061 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1064 mSettingsObserver = new SettingsObserver(mHandler);
1066 mArchive = new Archive(resources.getInteger(
1067 R.integer.config_notificationServiceArchiveSize));
1069 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1070 publishLocalService(NotificationManagerInternal.class, mInternalService);
1073 private void sendRegisteredOnlyBroadcast(String action) {
1074 getContext().sendBroadcastAsUser(new Intent(action)
1075 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1079 * Make sure the XML config and the the AppOps system agree about blocks.
1081 private void syncBlockDb() {
1084 // sync bans from ranker into app opps
1085 Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1086 for(Entry<Integer, String> ban : packageBans.entrySet()) {
1087 final int uid = ban.getKey();
1088 final String packageName = ban.getValue();
1089 setNotificationsEnabledForPackageImpl(packageName, uid, false);
1092 // sync bans from app opps into ranker
1093 packageBans.clear();
1094 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1095 final int userId = user.getUserHandle().getIdentifier();
1096 final PackageManager packageManager = getContext().getPackageManager();
1097 List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1098 final int packageCount = packages.size();
1099 for (int p = 0; p < packageCount; p++) {
1100 final String packageName = packages.get(p).packageName;
1102 final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1103 if (!checkNotificationOp(packageName, uid)) {
1104 packageBans.put(uid, packageName);
1106 } catch (NameNotFoundException e) {
1111 for (Entry<Integer, String> ban : packageBans.entrySet()) {
1112 mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1119 public void onBootPhase(int phase) {
1120 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1121 // no beeping until we're basically done booting
1122 mSystemReady = true;
1124 // Grab our optional AudioService
1125 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1126 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1127 mVrManagerInternal = getLocalService(VrManagerInternal.class);
1128 mZenModeHelper.onSystemReady();
1129 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1130 // This observer will force an update when observe is called, causing us to
1131 // bind to listener services.
1132 mSettingsObserver.observe();
1133 mListeners.onBootPhaseAppsCanStart();
1134 mRankerServices.onBootPhaseAppsCanStart();
1135 mConditionProviders.onBootPhaseAppsCanStart();
1139 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1140 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1142 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1143 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1145 // Now, cancel any outstanding notifications that are part of a just-disabled app
1146 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1147 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1148 REASON_PACKAGE_BANNED, null);
1152 private void updateListenerHintsLocked() {
1153 final int hints = calculateHints();
1154 if (hints == mListenerHints) return;
1155 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1156 mListenerHints = hints;
1157 scheduleListenerHintsChanged(hints);
1160 private void updateEffectsSuppressorLocked() {
1161 final long updatedSuppressedEffects = calculateSuppressedEffects();
1162 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1163 final List<ComponentName> suppressors = getSuppressors();
1164 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1165 mEffectsSuppressors = suppressors;
1166 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1167 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1170 private ArrayList<ComponentName> getSuppressors() {
1171 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1172 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1173 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1175 for (ManagedServiceInfo info : serviceInfoList) {
1176 names.add(info.component);
1183 private boolean removeDisabledHints(ManagedServiceInfo info) {
1184 return removeDisabledHints(info, 0);
1187 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1188 boolean removed = false;
1190 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1191 final int hint = mListenersDisablingEffects.keyAt(i);
1192 final ArraySet<ManagedServiceInfo> listeners =
1193 mListenersDisablingEffects.valueAt(i);
1195 if (hints == 0 || (hint & hints) == hint) {
1196 removed = removed || listeners.remove(info);
1203 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1204 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1205 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1208 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1209 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1212 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1213 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1217 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1218 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1219 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1222 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1223 hintListeners.add(info);
1226 private int calculateHints() {
1228 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1229 int hint = mListenersDisablingEffects.keyAt(i);
1230 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1232 if (!serviceInfoList.isEmpty()) {
1240 private long calculateSuppressedEffects() {
1241 int hints = calculateHints();
1242 long suppressedEffects = 0;
1244 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1245 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1248 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1249 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1252 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1253 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1256 return suppressedEffects;
1259 private void updateInterruptionFilterLocked() {
1260 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1261 if (interruptionFilter == mInterruptionFilter) return;
1262 mInterruptionFilter = interruptionFilter;
1263 scheduleInterruptionFilterChanged(interruptionFilter);
1266 private final IBinder mService = new INotificationManager.Stub() {
1268 // ============================================================================
1271 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1274 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1275 + " duration=" + duration);
1278 if (pkg == null || callback == null) {
1279 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1283 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1284 final boolean isPackageSuspended =
1285 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1287 if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
1288 || isPackageSuspended)) {
1289 if (!isSystemToast) {
1290 Slog.e(TAG, "Suppressing toast from package " + pkg
1291 + (isPackageSuspended
1292 ? " due to package suspended by administrator."
1293 : " by user request."));
1298 synchronized (mToastQueue) {
1299 int callingPid = Binder.getCallingPid();
1300 long callingId = Binder.clearCallingIdentity();
1303 int index = indexOfToastLocked(pkg, callback);
1304 // If it's already in the queue, we update it in place, we don't
1305 // move it to the end of the queue.
1307 record = mToastQueue.get(index);
1308 record.update(duration);
1310 // Limit the number of toasts that any given package except the android
1311 // package can enqueue. Prevents DOS attacks and deals with leaks.
1312 if (!isSystemToast) {
1314 final int N = mToastQueue.size();
1315 for (int i=0; i<N; i++) {
1316 final ToastRecord r = mToastQueue.get(i);
1317 if (r.pkg.equals(pkg)) {
1319 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1320 Slog.e(TAG, "Package has already posted " + count
1321 + " toasts. Not showing more. Package=" + pkg);
1328 record = new ToastRecord(callingPid, pkg, callback, duration);
1329 mToastQueue.add(record);
1330 index = mToastQueue.size() - 1;
1331 keepProcessAliveLocked(callingPid);
1333 // If it's at index 0, it's the current toast. It doesn't matter if it's
1334 // new or just been updated. Call back and tell it to show itself.
1335 // If the callback fails, this will remove it from the list, so don't
1336 // assume that it's valid after this.
1338 showNextToastLocked();
1341 Binder.restoreCallingIdentity(callingId);
1347 public void cancelToast(String pkg, ITransientNotification callback) {
1348 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1350 if (pkg == null || callback == null) {
1351 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1355 synchronized (mToastQueue) {
1356 long callingId = Binder.clearCallingIdentity();
1358 int index = indexOfToastLocked(pkg, callback);
1360 cancelToastLocked(index);
1362 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1363 + " callback=" + callback);
1366 Binder.restoreCallingIdentity(callingId);
1372 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1373 Notification notification, int[] idOut, int userId) throws RemoteException {
1374 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1375 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1379 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1380 checkCallerIsSystemOrSameApp(pkg);
1381 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1382 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1383 // Don't allow client applications to cancel foreground service notis or autobundled
1385 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1386 (Binder.getCallingUid() == Process.SYSTEM_UID
1387 ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
1388 | (Binder.getCallingUid() == Process.SYSTEM_UID
1389 ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
1390 REASON_APP_CANCEL, null);
1394 public void cancelAllNotifications(String pkg, int userId) {
1395 checkCallerIsSystemOrSameApp(pkg);
1397 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1398 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1400 // Calling from user space, don't allow the canceling of actively
1401 // running foreground services.
1402 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1403 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1404 REASON_APP_CANCEL_ALL, null);
1408 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1409 checkCallerIsSystem();
1411 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1412 mRankingHelper.setEnabled(pkg, uid, enabled);
1417 * Use this when you just want to know if notifications are OK for this package.
1420 public boolean areNotificationsEnabled(String pkg) {
1421 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1425 * Use this when you just want to know if notifications are OK for this package.
1428 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1429 checkCallerIsSystemOrSameApp(pkg);
1430 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1431 == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
1435 public void setPriority(String pkg, int uid, int priority) {
1436 checkCallerIsSystem();
1437 mRankingHelper.setPriority(pkg, uid, priority);
1442 public int getPriority(String pkg, int uid) {
1443 checkCallerIsSystem();
1444 return mRankingHelper.getPriority(pkg, uid);
1448 public void setVisibilityOverride(String pkg, int uid, int visibility) {
1449 checkCallerIsSystem();
1450 mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
1455 public int getVisibilityOverride(String pkg, int uid) {
1456 checkCallerIsSystem();
1457 return mRankingHelper.getVisibilityOverride(pkg, uid);
1461 public void setImportance(String pkg, int uid, int importance) {
1462 enforceSystemOrSystemUI("Caller not system or systemui");
1463 setNotificationsEnabledForPackageImpl(pkg, uid,
1464 importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1465 mRankingHelper.setImportance(pkg, uid, importance);
1470 public int getPackageImportance(String pkg) {
1471 checkCallerIsSystemOrSameApp(pkg);
1472 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1476 public int getImportance(String pkg, int uid) {
1477 enforceSystemOrSystemUI("Caller not system or systemui");
1478 return mRankingHelper.getImportance(pkg, uid);
1482 * System-only API for getting a list of current (i.e. not cleared) notifications.
1484 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1485 * @returns A list of all the notifications, in natural order.
1488 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1489 // enforce() will ensure the calling uid has the correct permission
1490 getContext().enforceCallingOrSelfPermission(
1491 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1492 "NotificationManagerService.getActiveNotifications");
1494 StatusBarNotification[] tmp = null;
1495 int uid = Binder.getCallingUid();
1497 // noteOp will check to make sure the callingPkg matches the uid
1498 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1499 == AppOpsManager.MODE_ALLOWED) {
1500 synchronized (mNotificationList) {
1501 tmp = new StatusBarNotification[mNotificationList.size()];
1502 final int N = mNotificationList.size();
1503 for (int i=0; i<N; i++) {
1504 tmp[i] = mNotificationList.get(i).sbn;
1512 * Public API for getting a list of current notifications for the calling package/uid.
1514 * @returns A list of all the package's notifications, in natural order.
1517 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1518 int incomingUserId) {
1519 checkCallerIsSystemOrSameApp(pkg);
1520 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1521 Binder.getCallingUid(), incomingUserId, true, false,
1522 "getAppActiveNotifications", pkg);
1524 final ArrayList<StatusBarNotification> list
1525 = new ArrayList<StatusBarNotification>(mNotificationList.size());
1527 synchronized (mNotificationList) {
1528 final int N = mNotificationList.size();
1529 for (int i = 0; i < N; i++) {
1530 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1531 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1532 && (sbn.getNotification().flags
1533 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1534 // We could pass back a cloneLight() but clients might get confused and
1535 // try to send this thing back to notify() again, which would not work
1537 final StatusBarNotification sbnOut = new StatusBarNotification(
1538 sbn.getPackageName(),
1540 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1541 0, // hide score from apps
1542 sbn.getNotification().clone(),
1543 sbn.getUser(), sbn.getPostTime());
1549 return new ParceledListSlice<StatusBarNotification>(list);
1553 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1555 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1558 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1559 // enforce() will ensure the calling uid has the correct permission
1560 getContext().enforceCallingOrSelfPermission(
1561 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1562 "NotificationManagerService.getHistoricalNotifications");
1564 StatusBarNotification[] tmp = null;
1565 int uid = Binder.getCallingUid();
1567 // noteOp will check to make sure the callingPkg matches the uid
1568 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1569 == AppOpsManager.MODE_ALLOWED) {
1570 synchronized (mArchive) {
1571 tmp = mArchive.getArray(count);
1578 * Register a listener binder directly with the notification manager.
1580 * Only works with system callers. Apps should extend
1581 * {@link android.service.notification.NotificationListenerService}.
1584 public void registerListener(final INotificationListener listener,
1585 final ComponentName component, final int userid) {
1586 enforceSystemOrSystemUI("INotificationManager.registerListener");
1587 mListeners.registerService(listener, component, userid);
1591 * Remove a listener binder directly
1594 public void unregisterListener(INotificationListener token, int userid) {
1595 mListeners.unregisterService(token, userid);
1599 * Allow an INotificationListener to simulate a "clear all" operation.
1601 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1603 * @param token The binder for the listener, to check that the caller is allowed
1606 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1607 final int callingUid = Binder.getCallingUid();
1608 final int callingPid = Binder.getCallingPid();
1609 long identity = Binder.clearCallingIdentity();
1611 synchronized (mNotificationList) {
1612 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1614 final int N = keys.length;
1615 for (int i = 0; i < N; i++) {
1616 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1617 if (r == null) continue;
1618 final int userId = r.sbn.getUserId();
1619 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1620 !mUserProfiles.isCurrentProfile(userId)) {
1621 throw new SecurityException("Disallowed call from listener: "
1624 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1625 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1629 cancelAllLocked(callingUid, callingPid, info.userid,
1630 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1634 Binder.restoreCallingIdentity(identity);
1639 * Handle request from an approved listener to re-enable itself.
1641 * @param component The componenet to be re-enabled, caller must match package.
1644 public void requestBindListener(ComponentName component) {
1645 checkCallerIsSystemOrSameApp(component.getPackageName());
1646 long identity = Binder.clearCallingIdentity();
1648 ManagedServices manager =
1649 mRankerServices.isComponentEnabledForCurrentProfiles(component)
1652 manager.setComponentState(component, true);
1654 Binder.restoreCallingIdentity(identity);
1659 public void requestUnbindListener(INotificationListener token) {
1660 long identity = Binder.clearCallingIdentity();
1662 // allow bound services to disable themselves
1663 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1664 info.getOwner().setComponentState(info.component, false);
1666 Binder.restoreCallingIdentity(identity);
1671 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1672 long identity = Binder.clearCallingIdentity();
1674 synchronized (mNotificationList) {
1675 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1677 final int N = keys.length;
1678 for (int i = 0; i < N; i++) {
1679 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1680 if (r == null) continue;
1681 final int userId = r.sbn.getUserId();
1682 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1683 !mUserProfiles.isCurrentProfile(userId)) {
1684 throw new SecurityException("Disallowed call from listener: "
1688 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1689 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1690 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
1692 UsageEvents.Event.USER_INTERACTION);
1699 Binder.restoreCallingIdentity(identity);
1703 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1704 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1705 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1706 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1708 userId, REASON_LISTENER_CANCEL, info);
1712 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1714 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1716 * @param token The binder for the listener, to check that the caller is allowed
1719 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1720 String tag, int id) {
1721 final int callingUid = Binder.getCallingUid();
1722 final int callingPid = Binder.getCallingPid();
1723 long identity = Binder.clearCallingIdentity();
1725 synchronized (mNotificationList) {
1726 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1727 if (info.supportsProfiles()) {
1728 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1729 + "from " + info.component
1730 + " use cancelNotification(key) instead.");
1732 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1733 pkg, tag, id, info.userid);
1737 Binder.restoreCallingIdentity(identity);
1742 * Allow an INotificationListener to request the list of outstanding notifications seen by
1743 * the current user. Useful when starting up, after which point the listener callbacks
1746 * @param token The binder for the listener, to check that the caller is allowed
1747 * @param keys An array of notification keys to fetch, or null to fetch everything
1748 * @returns The return value will contain the notifications specified in keys, in that
1749 * order, or if keys is null, all the notifications, in natural order.
1752 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1753 INotificationListener token, String[] keys, int trim) {
1754 synchronized (mNotificationList) {
1755 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1756 final boolean getKeys = keys != null;
1757 final int N = getKeys ? keys.length : mNotificationList.size();
1758 final ArrayList<StatusBarNotification> list
1759 = new ArrayList<StatusBarNotification>(N);
1760 for (int i=0; i<N; i++) {
1761 final NotificationRecord r = getKeys
1762 ? mNotificationsByKey.get(keys[i])
1763 : mNotificationList.get(i);
1764 if (r == null) continue;
1765 StatusBarNotification sbn = r.sbn;
1766 if (!isVisibleToListener(sbn, info)) continue;
1767 StatusBarNotification sbnToSend =
1768 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1769 list.add(sbnToSend);
1771 return new ParceledListSlice<StatusBarNotification>(list);
1776 public void requestHintsFromListener(INotificationListener token, int hints) {
1777 final long identity = Binder.clearCallingIdentity();
1779 synchronized (mNotificationList) {
1780 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1781 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
1782 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
1783 | HINT_HOST_DISABLE_CALL_EFFECTS;
1784 final boolean disableEffects = (hints & disableEffectsMask) != 0;
1785 if (disableEffects) {
1786 addDisabledHints(info, hints);
1788 removeDisabledHints(info, hints);
1790 updateListenerHintsLocked();
1791 updateEffectsSuppressorLocked();
1794 Binder.restoreCallingIdentity(identity);
1799 public int getHintsFromListener(INotificationListener token) {
1800 synchronized (mNotificationList) {
1801 return mListenerHints;
1806 public void requestInterruptionFilterFromListener(INotificationListener token,
1807 int interruptionFilter) throws RemoteException {
1808 final long identity = Binder.clearCallingIdentity();
1810 synchronized (mNotificationList) {
1811 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1812 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1813 updateInterruptionFilterLocked();
1816 Binder.restoreCallingIdentity(identity);
1821 public int getInterruptionFilterFromListener(INotificationListener token)
1822 throws RemoteException {
1823 synchronized (mNotificationLight) {
1824 return mInterruptionFilter;
1829 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1830 throws RemoteException {
1831 synchronized (mNotificationList) {
1832 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1833 if (info == null) return;
1834 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1839 public int getZenMode() {
1840 return mZenModeHelper.getZenMode();
1844 public ZenModeConfig getZenModeConfig() {
1845 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1846 return mZenModeHelper.getConfig();
1850 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1851 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1852 final long identity = Binder.clearCallingIdentity();
1854 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
1856 Binder.restoreCallingIdentity(identity);
1861 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
1862 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
1863 return mZenModeHelper.getZenRules();
1867 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1868 Preconditions.checkNotNull(id, "Id is null");
1869 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
1870 return mZenModeHelper.getAutomaticZenRule(id);
1874 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
1875 throws RemoteException {
1876 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1877 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1878 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1879 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1880 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
1882 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1883 "addAutomaticZenRule");
1887 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
1888 throws RemoteException {
1889 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1890 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1891 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1892 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1893 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
1895 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
1896 "updateAutomaticZenRule");
1900 public boolean removeAutomaticZenRule(String id) throws RemoteException {
1901 Preconditions.checkNotNull(id, "Id is null");
1902 // Verify that they can modify zen rules.
1903 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1905 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
1909 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1910 Preconditions.checkNotNull(packageName, "Package name is null");
1911 enforceSystemOrSystemUI("removeAutomaticZenRules");
1913 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1917 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
1918 Preconditions.checkNotNull(owner, "Owner is null");
1919 enforceSystemOrSystemUI("getRuleInstanceCount");
1921 return mZenModeHelper.getCurrentInstanceCount(owner);
1925 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1926 enforcePolicyAccess(pkg, "setInterruptionFilter");
1927 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1928 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1929 final long identity = Binder.clearCallingIdentity();
1931 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
1933 Binder.restoreCallingIdentity(identity);
1938 public void notifyConditions(final String pkg, IConditionProvider provider,
1939 final Condition[] conditions) {
1940 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1941 checkCallerIsSystemOrSameApp(pkg);
1942 mHandler.post(new Runnable() {
1945 mConditionProviders.notifyConditions(pkg, info, conditions);
1950 private void enforceSystemOrSystemUIOrVolume(String message) {
1951 if (mAudioManagerInternal != null) {
1952 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1953 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1957 enforceSystemOrSystemUI(message);
1960 private void enforceSystemOrSystemUI(String message) {
1961 if (isCallerSystem()) return;
1962 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1966 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
1968 checkCallerIsSystemOrSameApp(pkg);
1969 } catch (SecurityException e) {
1970 getContext().enforceCallingPermission(
1971 android.Manifest.permission.STATUS_BAR_SERVICE,
1976 private void enforcePolicyAccess(int uid, String method) {
1977 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1978 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1981 boolean accessAllowed = false;
1982 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1983 final int packageCount = packages.length;
1984 for (int i = 0; i < packageCount; i++) {
1985 if (checkPolicyAccess(packages[i])) {
1986 accessAllowed = true;
1989 if (!accessAllowed) {
1990 Slog.w(TAG, "Notification policy access denied calling " + method);
1991 throw new SecurityException("Notification policy access denied");
1995 private void enforcePolicyAccess(String pkg, String method) {
1996 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1997 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2000 if (!checkPolicyAccess(pkg)) {
2001 Slog.w(TAG, "Notification policy access denied calling " + method);
2002 throw new SecurityException("Notification policy access denied");
2006 private boolean checkPackagePolicyAccess(String pkg) {
2007 return mPolicyAccess.isPackageGranted(pkg);
2010 private boolean checkPolicyAccess(String pkg) {
2012 int uid = getContext().getPackageManager().getPackageUidAsUser(
2013 pkg, UserHandle.getCallingUserId());
2014 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2015 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2019 } catch (NameNotFoundException e) {
2022 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2026 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2027 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2028 != PackageManager.PERMISSION_GRANTED) {
2029 pw.println("Permission Denial: can't dump NotificationManager from pid="
2030 + Binder.getCallingPid()
2031 + ", uid=" + Binder.getCallingUid());
2035 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2036 if (filter != null && filter.stats) {
2037 dumpJson(pw, filter);
2039 dumpImpl(pw, filter);
2044 public ComponentName getEffectsSuppressor() {
2045 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
2046 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2050 public boolean matchesCallFilter(Bundle extras) {
2051 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2052 return mZenModeHelper.matchesCallFilter(
2053 Binder.getCallingUserHandle(),
2055 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2056 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2057 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2061 public boolean isSystemConditionProviderEnabled(String path) {
2062 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
2063 return mConditionProviders.isSystemProviderEnabled(path);
2066 // Backup/restore interface
2068 public byte[] getBackupPayload(int user) {
2069 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2070 //TODO: http://b/22388012
2071 if (user != UserHandle.USER_SYSTEM) {
2072 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2075 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2077 writePolicyXml(baos, true /*forBackup*/);
2078 return baos.toByteArray();
2079 } catch (IOException e) {
2080 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2086 public void applyRestore(byte[] payload, int user) {
2087 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2088 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2089 if (payload == null) {
2090 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2093 //TODO: http://b/22388012
2094 if (user != UserHandle.USER_SYSTEM) {
2095 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2098 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2100 readPolicyXml(bais, true /*forRestore*/);
2102 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2103 Slog.w(TAG, "applyRestore: error reading payload", e);
2108 public boolean isNotificationPolicyAccessGranted(String pkg) {
2109 return checkPolicyAccess(pkg);
2113 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2114 enforceSystemOrSystemUIOrSamePackage(pkg,
2115 "request policy access status for another package");
2116 return checkPolicyAccess(pkg);
2120 public String[] getPackagesRequestingNotificationPolicyAccess()
2121 throws RemoteException {
2122 enforceSystemOrSystemUI("request policy access packages");
2123 final long identity = Binder.clearCallingIdentity();
2125 return mPolicyAccess.getRequestingPackages();
2127 Binder.restoreCallingIdentity(identity);
2132 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2133 throws RemoteException {
2134 enforceSystemOrSystemUI("grant notification policy access");
2135 final long identity = Binder.clearCallingIdentity();
2137 synchronized (mNotificationList) {
2138 mPolicyAccess.put(pkg, granted);
2141 Binder.restoreCallingIdentity(identity);
2146 public Policy getNotificationPolicy(String pkg) {
2147 enforcePolicyAccess(pkg, "getNotificationPolicy");
2148 final long identity = Binder.clearCallingIdentity();
2150 return mZenModeHelper.getNotificationPolicy();
2152 Binder.restoreCallingIdentity(identity);
2157 public void setNotificationPolicy(String pkg, Policy policy) {
2158 enforcePolicyAccess(pkg, "setNotificationPolicy");
2159 final long identity = Binder.clearCallingIdentity();
2161 mZenModeHelper.setNotificationPolicy(policy);
2163 Binder.restoreCallingIdentity(identity);
2168 public void applyAdjustmentFromRankerService(INotificationListener token,
2169 Adjustment adjustment) throws RemoteException {
2170 final long identity = Binder.clearCallingIdentity();
2172 synchronized (mNotificationList) {
2173 mRankerServices.checkServiceTokenLocked(token);
2174 applyAdjustmentLocked(adjustment);
2176 maybeAddAutobundleSummary(adjustment);
2177 mRankingHandler.requestSort();
2179 Binder.restoreCallingIdentity(identity);
2184 public void applyAdjustmentsFromRankerService(INotificationListener token,
2185 List<Adjustment> adjustments) throws RemoteException {
2187 final long identity = Binder.clearCallingIdentity();
2189 synchronized (mNotificationList) {
2190 mRankerServices.checkServiceTokenLocked(token);
2191 for (Adjustment adjustment : adjustments) {
2192 applyAdjustmentLocked(adjustment);
2195 for (Adjustment adjustment : adjustments) {
2196 maybeAddAutobundleSummary(adjustment);
2198 mRankingHandler.requestSort();
2200 Binder.restoreCallingIdentity(identity);
2205 private void applyAdjustmentLocked(Adjustment adjustment) {
2206 maybeClearAutobundleSummaryLocked(adjustment);
2207 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2211 if (adjustment.getImportance() != IMPORTANCE_NONE) {
2212 n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
2214 if (adjustment.getSignals() != null) {
2215 Bundle.setDefusable(adjustment.getSignals(), true);
2216 final String autoGroupKey = adjustment.getSignals().getString(
2217 Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2218 if (autoGroupKey == null) {
2219 EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
2221 EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
2223 n.sbn.setOverrideGroupKey(autoGroupKey);
2227 // Clears the 'fake' auto-bunding summary.
2228 private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
2229 if (adjustment.getSignals() != null) {
2230 Bundle.setDefusable(adjustment.getSignals(), true);
2231 if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
2232 && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2233 ArrayMap<String, String> summaries =
2234 mAutobundledSummaries.get(adjustment.getUser());
2235 if (summaries != null && summaries.containsKey(adjustment.getPackage())) {
2237 final NotificationRecord removed = mNotificationsByKey.get(
2238 summaries.remove(adjustment.getPackage()));
2239 if (removed != null) {
2240 mNotificationList.remove(removed);
2241 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2248 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2249 private void maybeAddAutobundleSummary(Adjustment adjustment) {
2250 if (adjustment.getSignals() != null) {
2251 Bundle.setDefusable(adjustment.getSignals(), true);
2252 if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2253 final String newAutoBundleKey =
2254 adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2256 NotificationRecord summaryRecord = null;
2257 synchronized (mNotificationList) {
2258 NotificationRecord notificationRecord =
2259 mNotificationsByKey.get(adjustment.getKey());
2260 if (notificationRecord == null) {
2261 // The notification could have been cancelled again already. A successive
2262 // adjustment will post a summary if needed.
2265 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2266 userId = adjustedSbn.getUser().getIdentifier();
2267 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2268 if (summaries == null) {
2269 summaries = new ArrayMap<>();
2271 mAutobundledSummaries.put(userId, summaries);
2272 if (!summaries.containsKey(adjustment.getPackage())
2273 && newAutoBundleKey != null) {
2275 final ApplicationInfo appInfo =
2276 adjustedSbn.getNotification().extras.getParcelable(
2277 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2278 final Bundle extras = new Bundle();
2279 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2280 final Notification summaryNotification =
2281 new Notification.Builder(getContext()).setSmallIcon(
2282 adjustedSbn.getNotification().getSmallIcon())
2283 .setGroupSummary(true)
2284 .setGroup(newAutoBundleKey)
2285 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2286 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2287 .setColor(adjustedSbn.getNotification().color)
2289 summaryNotification.extras.putAll(extras);
2290 Intent appIntent = getContext().getPackageManager()
2291 .getLaunchIntentForPackage(adjustment.getPackage());
2292 if (appIntent != null) {
2293 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2294 getContext(), 0, appIntent, 0, null,
2295 UserHandle.of(userId));
2297 final StatusBarNotification summarySbn =
2298 new StatusBarNotification(adjustedSbn.getPackageName(),
2299 adjustedSbn.getOpPkg(),
2300 Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2301 adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2302 summaryNotification, adjustedSbn.getUser(),
2304 System.currentTimeMillis());
2305 summaryRecord = new NotificationRecord(getContext(), summarySbn);
2306 summaries.put(adjustment.getPackage(), summarySbn.getKey());
2309 if (summaryRecord != null) {
2310 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2316 private String disableNotificationEffects(NotificationRecord record) {
2317 if (mDisableNotificationEffects) {
2318 return "booleanState";
2320 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2321 return "listenerHints";
2323 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2329 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2330 JSONObject dump = new JSONObject();
2332 dump.put("service", "Notification Manager");
2333 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2334 dump.put("ranking", mRankingHelper.dumpJson(filter));
2335 dump.put("stats", mUsageStats.dumpJson(filter));
2336 } catch (JSONException e) {
2337 e.printStackTrace();
2342 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2343 pw.print("Current Notification Manager state");
2344 if (filter.filtered) {
2345 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2349 final boolean zenOnly = filter.filtered && filter.zen;
2352 synchronized (mToastQueue) {
2353 N = mToastQueue.size();
2355 pw.println(" Toast Queue:");
2356 for (int i=0; i<N; i++) {
2357 mToastQueue.get(i).dump(pw, " ", filter);
2364 synchronized (mNotificationList) {
2366 N = mNotificationList.size();
2368 pw.println(" Notification List:");
2369 for (int i=0; i<N; i++) {
2370 final NotificationRecord nr = mNotificationList.get(i);
2371 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2372 nr.dump(pw, " ", getContext(), filter.redact);
2377 if (!filter.filtered) {
2380 pw.println(" Lights List:");
2381 for (int i=0; i<N; i++) {
2387 pw.println(mLights.get(i));
2391 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
2392 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
2393 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
2394 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
2395 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
2396 pw.println(" mCallState=" + callStateToString(mCallState));
2397 pw.println(" mSystemReady=" + mSystemReady);
2398 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
2400 pw.println(" mArchive=" + mArchive.toString());
2401 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2403 while (iter.hasNext()) {
2404 final StatusBarNotification sbn = iter.next();
2405 if (filter != null && !filter.matches(sbn)) continue;
2406 pw.println(" " + sbn);
2408 if (iter.hasNext()) pw.println(" ...");
2415 pw.println("\n Usage Stats:");
2416 mUsageStats.dump(pw, " ", filter);
2419 if (!filter.filtered || zenOnly) {
2420 pw.println("\n Zen Mode:");
2421 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
2422 mZenModeHelper.dump(pw, " ");
2424 pw.println("\n Zen Log:");
2425 ZenLog.dump(pw, " ");
2429 pw.println("\n Ranking Config:");
2430 mRankingHelper.dump(pw, " ", filter);
2432 pw.println("\n Notification listeners:");
2433 mListeners.dump(pw, filter);
2434 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2435 pw.print(" mListenersDisablingEffects: (");
2436 N = mListenersDisablingEffects.size();
2437 for (int i = 0; i < N; i++) {
2438 final int hint = mListenersDisablingEffects.keyAt(i);
2439 if (i > 0) pw.print(';');
2440 pw.print("hint[" + hint + "]:");
2442 final ArraySet<ManagedServiceInfo> listeners =
2443 mListenersDisablingEffects.valueAt(i);
2444 final int listenerSize = listeners.size();
2446 for (int j = 0; j < listenerSize; j++) {
2447 if (i > 0) pw.print(',');
2448 final ManagedServiceInfo listener = listeners.valueAt(i);
2449 pw.print(listener.component);
2453 pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
2454 pw.println("\n Notification ranker services:");
2455 mRankerServices.dump(pw, filter);
2457 pw.println("\n Policy access:");
2458 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
2460 pw.println("\n Condition providers:");
2461 mConditionProviders.dump(pw, filter);
2463 pw.println("\n Group summaries:");
2464 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2465 NotificationRecord r = entry.getValue();
2466 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2467 if (mNotificationsByKey.get(r.getKey()) != r) {
2468 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
2469 r.dump(pw, " ", getContext(), filter.redact);
2476 * The private API only accessible to the system process.
2478 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2480 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2481 String tag, int id, Notification notification, int[] idReceived, int userId) {
2482 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2483 idReceived, userId);
2487 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2489 checkCallerIsSystem();
2490 synchronized (mNotificationList) {
2491 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2493 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2494 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2497 NotificationRecord r = mNotificationList.get(i);
2498 StatusBarNotification sbn = r.sbn;
2499 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2500 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2501 // we have to revert to the flags we received initially *and* force remove
2502 // FLAG_FOREGROUND_SERVICE.
2503 sbn.getNotification().flags =
2504 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2505 mRankingHelper.sort(mNotificationList);
2506 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2511 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2512 final int callingPid, final String tag, final int id, final Notification notification,
2513 int[] idOut, int incomingUserId) {
2515 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2516 + " notification=" + notification);
2518 checkCallerIsSystemOrSameApp(pkg);
2519 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2520 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2522 final int userId = ActivityManager.handleIncomingUser(callingPid,
2523 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2524 final UserHandle user = new UserHandle(userId);
2526 // Fix the notification as best we can.
2528 final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2529 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2530 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2531 Notification.addFieldsFromContext(ai, userId, notification);
2532 } catch (NameNotFoundException e) {
2533 Slog.e(TAG, "Cannot create a context for sending app", e);
2537 mUsageStats.registerEnqueuedByApp(pkg);
2539 // Limit the number of notifications that any given package except the android
2540 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2541 if (!isSystemNotification && !isNotificationFromListener) {
2542 synchronized (mNotificationList) {
2543 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
2544 if (appEnqueueRate > mMaxPackageEnqueueRate) {
2545 mUsageStats.registerOverRateQuota(pkg);
2546 final long now = SystemClock.elapsedRealtime();
2547 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
2548 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
2549 + ". Shedding events. package=" + pkg);
2550 mLastOverRateLogTime = now;
2556 final int N = mNotificationList.size();
2557 for (int i=0; i<N; i++) {
2558 final NotificationRecord r = mNotificationList.get(i);
2559 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2560 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2561 break; // Allow updating existing notification
2564 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2565 mUsageStats.registerOverCountQuota(pkg);
2566 Slog.e(TAG, "Package has already posted " + count
2567 + " notifications. Not showing more. package=" + pkg);
2575 if (pkg == null || notification == null) {
2576 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2577 + " id=" + id + " notification=" + notification);
2580 // Whitelist pending intents.
2581 if (notification.allPendingIntents != null) {
2582 final int intentCount = notification.allPendingIntents.size();
2583 if (intentCount > 0) {
2584 final ActivityManagerInternal am = LocalServices
2585 .getService(ActivityManagerInternal.class);
2586 final long duration = LocalServices.getService(
2587 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
2588 for (int i = 0; i < intentCount; i++) {
2589 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
2590 if (pendingIntent != null) {
2591 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
2598 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2599 Notification.PRIORITY_MAX);
2601 // setup local book-keeping
2602 final StatusBarNotification n = new StatusBarNotification(
2603 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2605 final NotificationRecord r = new NotificationRecord(getContext(), n);
2606 mHandler.post(new EnqueueNotificationRunnable(userId, r));
2611 private class EnqueueNotificationRunnable implements Runnable {
2612 private final NotificationRecord r;
2613 private final int userId;
2615 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2616 this.userId = userId;
2623 synchronized (mNotificationList) {
2624 final StatusBarNotification n = r.sbn;
2625 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
2626 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2628 // Retain ranking information from previous record
2629 r.copyRankingInformation(old);
2632 final int callingUid = n.getUid();
2633 final int callingPid = n.getInitialPid();
2634 final Notification notification = n.getNotification();
2635 final String pkg = n.getPackageName();
2636 final int id = n.getId();
2637 final String tag = n.getTag();
2638 final boolean isSystemNotification = isUidSystem(callingUid) ||
2639 ("android".equals(pkg));
2641 // Handle grouped notifications and bail out early if we
2642 // can to avoid extracting signals.
2643 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2645 // This conditional is a dirty hack to limit the logging done on
2646 // behalf of the download manager without affecting other apps.
2647 if (!pkg.equals("com.android.providers.downloads")
2648 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2649 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2651 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2653 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2654 pkg, id, tag, userId, notification.toString(),
2658 mRankingHelper.extractSignals(r);
2660 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
2663 if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
2664 || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
2665 if (!isSystemNotification) {
2666 if (isPackageSuspended) {
2667 Slog.e(TAG, "Suppressing notification from package due to package "
2668 + "suspended by administrator.");
2669 mUsageStats.registerSuspendedByAdmin(r);
2671 Slog.e(TAG, "Suppressing notification from package by user request.");
2672 mUsageStats.registerBlocked(r);
2678 // tell the ranker service about the notification
2679 if (mRankerServices.isEnabled()) {
2680 mRankerServices.onNotificationEnqueued(r);
2681 // TODO delay the code below here for 100ms or until there is an answer
2685 int index = indexOfNotificationLocked(n.getKey());
2687 mNotificationList.add(r);
2688 mUsageStats.registerPostedByApp(r);
2690 old = mNotificationList.get(index);
2691 mNotificationList.set(index, r);
2692 mUsageStats.registerUpdatedByApp(r, old);
2693 // Make sure we don't lose the foreground service state.
2694 notification.flags |=
2695 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2699 mNotificationsByKey.put(n.getKey(), r);
2701 // Ensure if this is a foreground service that the proper additional
2703 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2704 notification.flags |= Notification.FLAG_ONGOING_EVENT
2705 | Notification.FLAG_NO_CLEAR;
2708 applyZenModeLocked(r);
2709 mRankingHelper.sort(mNotificationList);
2711 if (notification.getSmallIcon() != null) {
2712 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2713 mListeners.notifyPostedLocked(n, oldSbn);
2715 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2716 if (old != null && !old.isCanceled) {
2717 mListeners.notifyRemovedLocked(n);
2719 // ATTENTION: in a future release we will bail out here
2720 // so that we do not play sounds, show lights, etc. for invalid
2722 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2723 + n.getPackageName());
2726 buzzBeepBlinkLocked(r);
2732 * Ensures that grouped notification receive their special treatment.
2734 * <p>Cancels group children if the new notification causes a group to lose
2737 * <p>Updates mSummaryByGroupKey.</p>
2739 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2740 int callingUid, int callingPid) {
2741 StatusBarNotification sbn = r.sbn;
2742 Notification n = sbn.getNotification();
2743 if (n.isGroupSummary() && !sbn.isAppGroup()) {
2744 // notifications without a group shouldn't be a summary, otherwise autobundling can
2746 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
2749 String group = sbn.getGroupKey();
2750 boolean isSummary = n.isGroupSummary();
2752 Notification oldN = old != null ? old.sbn.getNotification() : null;
2753 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2754 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2757 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2758 if (removedSummary != old) {
2760 removedSummary != null ? removedSummary.getKey() : "<null>";
2761 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2762 ", removed=" + removedKey);
2766 mSummaryByGroupKey.put(group, r);
2769 // Clear out group children of the old notification if the update
2770 // causes the group summary to go away. This happens when the old
2771 // notification was a summary and the new one isn't, or when the old
2772 // notification was a summary and its group key changed.
2773 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2774 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2775 REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
2780 void buzzBeepBlinkLocked(NotificationRecord record) {
2781 boolean buzz = false;
2782 boolean beep = false;
2783 boolean blink = false;
2785 final Notification notification = record.sbn.getNotification();
2786 final String key = record.getKey();
2788 // Should this notification make noise, vibe, or use the LED?
2789 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
2790 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2791 if (DBG || record.isIntercepted())
2793 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2794 " intercept=" + record.isIntercepted()
2797 final int currentUser;
2798 final long token = Binder.clearCallingIdentity();
2800 currentUser = ActivityManager.getCurrentUser();
2802 Binder.restoreCallingIdentity(token);
2805 // If we're not supposed to beep, vibrate, etc. then don't.
2806 final String disableEffects = disableNotificationEffects(record);
2807 if (disableEffects != null) {
2808 ZenLog.traceDisableEffects(record, disableEffects);
2811 // Remember if this notification already owns the notification channels.
2812 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2813 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2815 // These are set inside the conditional if the notification is allowed to make noise.
2816 boolean hasValidVibrate = false;
2817 boolean hasValidSound = false;
2818 if (disableEffects == null
2819 && (record.getUserId() == UserHandle.USER_ALL ||
2820 record.getUserId() == currentUser ||
2821 mUserProfiles.isCurrentProfile(record.getUserId()))
2824 && mAudioManager != null) {
2825 if (DBG) Slog.v(TAG, "Interrupting!");
2827 // should we use the default notification sound? (indicated either by
2828 // DEFAULT_SOUND or because notification.sound is pointing at
2829 // Settings.System.NOTIFICATION_SOUND)
2830 final boolean useDefaultSound =
2831 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2832 Settings.System.DEFAULT_NOTIFICATION_URI
2833 .equals(notification.sound);
2835 Uri soundUri = null;
2836 if (useDefaultSound) {
2837 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2839 // check to see if the default notification sound is silent
2840 hasValidSound = mSystemNotificationSound != null;
2841 } else if (notification.sound != null) {
2842 soundUri = notification.sound;
2843 hasValidSound = (soundUri != null);
2846 // Does the notification want to specify its own vibration?
2847 final boolean hasCustomVibrate = notification.vibrate != null;
2849 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2850 // mode, and no other vibration is specified, we fall back to vibration
2851 final boolean convertSoundToVibration =
2854 && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
2856 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2857 final boolean useDefaultVibrate =
2858 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2860 hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
2863 // We can alert, and we're allowed to alert, but if the developer asked us to only do
2864 // it once, and we already have, then don't.
2865 if (!(record.isUpdate
2866 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
2868 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2870 if (hasValidSound) {
2872 (notification.flags & Notification.FLAG_INSISTENT) != 0;
2873 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2874 mSoundNotificationKey = key;
2875 // do not play notifications if stream volume is 0 (typically because
2876 // ringer mode is silent) or if there is a user of exclusive audio focus
2877 if ((mAudioManager.getStreamVolume(
2878 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2879 && !mAudioManager.isAudioFocusExclusive()) {
2880 final long identity = Binder.clearCallingIdentity();
2882 final IRingtonePlayer player =
2883 mAudioManager.getRingtonePlayer();
2884 if (player != null) {
2885 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2886 + " with attributes " + audioAttributes);
2887 player.playAsync(soundUri, record.sbn.getUser(), looping,
2891 } catch (RemoteException e) {
2893 Binder.restoreCallingIdentity(identity);
2898 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
2899 == AudioManager.RINGER_MODE_SILENT)) {
2900 mVibrateNotificationKey = key;
2902 if (useDefaultVibrate || convertSoundToVibration) {
2903 // Escalate privileges so we can use the vibrator even if the
2904 // notifying app does not have the VIBRATE permission.
2905 long identity = Binder.clearCallingIdentity();
2907 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2908 useDefaultVibrate ? mDefaultVibrationPattern
2909 : mFallbackVibrationPattern,
2910 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2911 ? 0: -1, audioAttributesForNotification(notification));
2914 Binder.restoreCallingIdentity(identity);
2916 } else if (notification.vibrate.length > 1) {
2917 // If you want your own vibration pattern, you need the VIBRATE
2919 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2920 notification.vibrate,
2921 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2922 ? 0: -1, audioAttributesForNotification(notification));
2929 // If a notification is updated to remove the actively playing sound or vibrate,
2930 // cancel that feedback now
2931 if (wasBeep && !hasValidSound) {
2934 if (wasBuzz && !hasValidVibrate) {
2935 clearVibrateLocked();
2939 // release the light
2940 boolean wasShowLights = mLights.remove(key);
2941 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2942 && ((record.getSuppressedVisualEffects()
2943 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
2945 updateLightsLocked();
2946 if (mUseAttentionLight) {
2947 mAttentionLight.pulse();
2950 } else if (wasShowLights) {
2951 updateLightsLocked();
2953 if (buzz || beep || blink) {
2954 if (((record.getSuppressedVisualEffects()
2955 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
2956 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
2958 EventLogTags.writeNotificationAlert(key,
2959 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2960 mHandler.post(mBuzzBeepBlinked);
2965 private static AudioAttributes audioAttributesForNotification(Notification n) {
2966 if (n.audioAttributes != null
2967 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2968 // the audio attributes are set and different from the default, use them
2969 return n.audioAttributes;
2970 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2971 // the stream type is valid, use it
2972 return new AudioAttributes.Builder()
2973 .setInternalLegacyStreamType(n.audioStreamType)
2975 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2976 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2978 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2979 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2983 void showNextToastLocked() {
2984 ToastRecord record = mToastQueue.get(0);
2985 while (record != null) {
2986 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2988 record.callback.show();
2989 scheduleTimeoutLocked(record);
2991 } catch (RemoteException e) {
2992 Slog.w(TAG, "Object died trying to show notification " + record.callback
2993 + " in package " + record.pkg);
2994 // remove it from the list and let the process die
2995 int index = mToastQueue.indexOf(record);
2997 mToastQueue.remove(index);
2999 keepProcessAliveLocked(record.pid);
3000 if (mToastQueue.size() > 0) {
3001 record = mToastQueue.get(0);
3009 void cancelToastLocked(int index) {
3010 ToastRecord record = mToastQueue.get(index);
3012 record.callback.hide();
3013 } catch (RemoteException e) {
3014 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3015 + " in package " + record.pkg);
3016 // don't worry about this, we're about to remove it from
3019 mToastQueue.remove(index);
3020 keepProcessAliveLocked(record.pid);
3021 if (mToastQueue.size() > 0) {
3022 // Show the next one. If the callback fails, this will remove
3023 // it from the list, so don't assume that the list hasn't changed
3024 // after this point.
3025 showNextToastLocked();
3029 private void scheduleTimeoutLocked(ToastRecord r)
3031 mHandler.removeCallbacksAndMessages(r);
3032 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3033 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3034 mHandler.sendMessageDelayed(m, delay);
3037 private void handleTimeout(ToastRecord record)
3039 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3040 synchronized (mToastQueue) {
3041 int index = indexOfToastLocked(record.pkg, record.callback);
3043 cancelToastLocked(index);
3048 // lock on mToastQueue
3049 int indexOfToastLocked(String pkg, ITransientNotification callback)
3051 IBinder cbak = callback.asBinder();
3052 ArrayList<ToastRecord> list = mToastQueue;
3053 int len = list.size();
3054 for (int i=0; i<len; i++) {
3055 ToastRecord r = list.get(i);
3056 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3063 // lock on mToastQueue
3064 void keepProcessAliveLocked(int pid)
3066 int toastCount = 0; // toasts from this pid
3067 ArrayList<ToastRecord> list = mToastQueue;
3068 int N = list.size();
3069 for (int i=0; i<N; i++) {
3070 ToastRecord r = list.get(i);
3076 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
3077 } catch (RemoteException e) {
3078 // Shouldn't happen.
3082 private void handleRankingReconsideration(Message message) {
3083 if (!(message.obj instanceof RankingReconsideration)) return;
3084 RankingReconsideration recon = (RankingReconsideration) message.obj;
3087 synchronized (mNotificationList) {
3088 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3089 if (record == null) {
3092 int indexBefore = findNotificationRecordIndexLocked(record);
3093 boolean interceptBefore = record.isIntercepted();
3094 int visibilityBefore = record.getPackageVisibilityOverride();
3095 recon.applyChangesLocked(record);
3096 applyZenModeLocked(record);
3097 mRankingHelper.sort(mNotificationList);
3098 int indexAfter = findNotificationRecordIndexLocked(record);
3099 boolean interceptAfter = record.isIntercepted();
3100 int visibilityAfter = record.getPackageVisibilityOverride();
3101 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3102 || visibilityBefore != visibilityAfter;
3103 if (interceptBefore && !interceptAfter) {
3104 buzzBeepBlinkLocked(record);
3108 scheduleSendRankingUpdate();
3112 private void handleRankingSort() {
3113 synchronized (mNotificationList) {
3114 final int N = mNotificationList.size();
3115 ArrayList<String> orderBefore = new ArrayList<String>(N);
3116 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
3117 int[] visibilities = new int[N];
3118 int[] importances = new int[N];
3119 for (int i = 0; i < N; i++) {
3120 final NotificationRecord r = mNotificationList.get(i);
3121 orderBefore.add(r.getKey());
3122 groupOverrideBefore.add(r.sbn.getGroupKey());
3123 visibilities[i] = r.getPackageVisibilityOverride();
3124 importances[i] = r.getImportance();
3125 mRankingHelper.extractSignals(r);
3127 mRankingHelper.sort(mNotificationList);
3128 for (int i = 0; i < N; i++) {
3129 final NotificationRecord r = mNotificationList.get(i);
3130 if (!orderBefore.get(i).equals(r.getKey())
3131 || visibilities[i] != r.getPackageVisibilityOverride()
3132 || importances[i] != r.getImportance()
3133 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
3134 scheduleSendRankingUpdate();
3141 // let zen mode evaluate this record
3142 private void applyZenModeLocked(NotificationRecord record) {
3143 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
3144 if (record.isIntercepted()) {
3145 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3146 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3147 | (mZenModeHelper.shouldSuppressWhenScreenOn()
3148 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
3149 record.setSuppressedVisualEffects(suppressed);
3153 // lock on mNotificationList
3154 private int findNotificationRecordIndexLocked(NotificationRecord target) {
3155 return mRankingHelper.indexOf(mNotificationList, target);
3158 private void scheduleSendRankingUpdate() {
3159 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3160 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3161 mHandler.sendMessage(m);
3165 private void handleSendRankingUpdate() {
3166 synchronized (mNotificationList) {
3167 mListeners.notifyRankingUpdateLocked();
3171 private void scheduleListenerHintsChanged(int state) {
3172 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3173 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
3176 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3177 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3178 mHandler.obtainMessage(
3179 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3180 listenerInterruptionFilter,
3184 private void handleListenerHintsChanged(int hints) {
3185 synchronized (mNotificationList) {
3186 mListeners.notifyListenerHintsChangedLocked(hints);
3190 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3191 synchronized (mNotificationList) {
3192 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3196 private final class WorkerHandler extends Handler
3199 public void handleMessage(Message msg)
3203 case MESSAGE_TIMEOUT:
3204 handleTimeout((ToastRecord)msg.obj);
3206 case MESSAGE_SAVE_POLICY_FILE:
3207 handleSavePolicyFile();
3209 case MESSAGE_SEND_RANKING_UPDATE:
3210 handleSendRankingUpdate();
3212 case MESSAGE_LISTENER_HINTS_CHANGED:
3213 handleListenerHintsChanged(msg.arg1);
3215 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3216 handleListenerInterruptionFilterChanged(msg.arg1);
3223 private final class RankingHandlerWorker extends Handler implements RankingHandler
3225 public RankingHandlerWorker(Looper looper) {
3230 public void handleMessage(Message msg) {
3232 case MESSAGE_RECONSIDER_RANKING:
3233 handleRankingReconsideration(msg);
3235 case MESSAGE_RANKING_SORT:
3236 handleRankingSort();
3241 public void requestSort() {
3242 removeMessages(MESSAGE_RANKING_SORT);
3243 sendEmptyMessage(MESSAGE_RANKING_SORT);
3246 public void requestReconsideration(RankingReconsideration recon) {
3247 Message m = Message.obtain(this,
3248 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3249 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3250 sendMessageDelayed(m, delay);
3255 // ============================================================================
3256 static int clamp(int x, int low, int high) {
3257 return (x < low) ? low : ((x > high) ? high : x);
3260 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3261 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
3262 if (!manager.isEnabled()) {
3266 AccessibilityEvent event =
3267 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3268 event.setPackageName(packageName);
3269 event.setClassName(Notification.class.getName());
3270 event.setParcelableData(notification);
3271 CharSequence tickerText = notification.tickerText;
3272 if (!TextUtils.isEmpty(tickerText)) {
3273 event.getText().add(tickerText);
3276 manager.sendAccessibilityEvent(event);
3279 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
3282 if (r.getNotification().deleteIntent != null) {
3284 r.getNotification().deleteIntent.send();
3285 } catch (PendingIntent.CanceledException ex) {
3286 // do nothing - there's no relevant way to recover, and
3287 // no reason to let this propagate
3288 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
3294 if (r.getNotification().getSmallIcon() != null) {
3295 r.isCanceled = true;
3296 mListeners.notifyRemovedLocked(r.sbn);
3299 final String canceledKey = r.getKey();
3302 if (canceledKey.equals(mSoundNotificationKey)) {
3303 mSoundNotificationKey = null;
3304 final long identity = Binder.clearCallingIdentity();
3306 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3307 if (player != null) {
3310 } catch (RemoteException e) {
3312 Binder.restoreCallingIdentity(identity);
3317 if (canceledKey.equals(mVibrateNotificationKey)) {
3318 mVibrateNotificationKey = null;
3319 long identity = Binder.clearCallingIdentity();
3324 Binder.restoreCallingIdentity(identity);
3329 mLights.remove(canceledKey);
3331 // Record usage stats
3332 // TODO: add unbundling stats?
3334 case REASON_DELEGATE_CANCEL:
3335 case REASON_DELEGATE_CANCEL_ALL:
3336 case REASON_LISTENER_CANCEL:
3337 case REASON_LISTENER_CANCEL_ALL:
3338 mUsageStats.registerDismissedByUser(r);
3340 case REASON_APP_CANCEL:
3341 case REASON_APP_CANCEL_ALL:
3342 mUsageStats.registerRemovedByApp(r);
3346 mNotificationsByKey.remove(r.sbn.getKey());
3347 String groupKey = r.getGroupKey();
3348 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3349 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3350 mSummaryByGroupKey.remove(groupKey);
3352 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
3353 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
3354 summaries.remove(r.sbn.getPackageName());
3357 // Save it for users of getHistoricalNotifications()
3358 mArchive.record(r.sbn);
3360 final long now = System.currentTimeMillis();
3361 EventLogTags.writeNotificationCanceled(canceledKey, reason,
3362 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
3366 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
3367 * and none of the {@code mustNotHaveFlags}.
3369 void cancelNotification(final int callingUid, final int callingPid,
3370 final String pkg, final String tag, final int id,
3371 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
3372 final int userId, final int reason, final ManagedServiceInfo listener) {
3373 // In enqueueNotificationInternal notifications are added by scheduling the
3374 // work on the worker handler. Hence, we also schedule the cancel on this
3375 // handler to avoid a scenario where an add notification call followed by a
3376 // remove notification call ends up in not removing the notification.
3377 mHandler.post(new Runnable() {
3380 String listenerName = listener == null ? null : listener.component.toShortString();
3381 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3382 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
3384 synchronized (mNotificationList) {
3385 int index = indexOfNotificationLocked(pkg, tag, id, userId);
3387 NotificationRecord r = mNotificationList.get(index);
3389 // Ideally we'd do this in the caller of this method. However, that would
3390 // require the caller to also find the notification.
3391 if (reason == REASON_DELEGATE_CLICK) {
3392 mUsageStats.registerClickedByUser(r);
3395 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3398 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3402 mNotificationList.remove(index);
3404 cancelNotificationLocked(r, sendDelete, reason);
3405 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3406 REASON_GROUP_SUMMARY_CANCELED, sendDelete);
3407 updateLightsLocked();
3415 * Determine whether the userId applies to the notification in question, either because
3416 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3418 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3420 // looking for USER_ALL notifications? match everything
3421 userId == UserHandle.USER_ALL
3422 // a notification sent to USER_ALL matches any query
3423 || r.getUserId() == UserHandle.USER_ALL
3424 // an exact user match
3425 || r.getUserId() == userId;
3429 * Determine whether the userId applies to the notification in question, either because
3430 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
3431 * because it matches one of the users profiles.
3433 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
3434 return notificationMatchesUserId(r, userId)
3435 || mUserProfiles.isCurrentProfile(r.getUserId());
3439 * Cancels all notifications from a given package that have all of the
3440 * {@code mustHaveFlags}.
3442 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
3443 int mustNotHaveFlags, boolean doit, int userId, int reason,
3444 ManagedServiceInfo listener) {
3445 String listenerName = listener == null ? null : listener.component.toShortString();
3446 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3447 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
3450 synchronized (mNotificationList) {
3451 final int N = mNotificationList.size();
3452 ArrayList<NotificationRecord> canceledNotifications = null;
3453 for (int i = N-1; i >= 0; --i) {
3454 NotificationRecord r = mNotificationList.get(i);
3455 if (!notificationMatchesUserId(r, userId)) {
3458 // Don't remove notifications to all, if there's no package name specified
3459 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
3462 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
3465 if ((r.getFlags() & mustNotHaveFlags) != 0) {
3468 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
3471 if (canceledNotifications == null) {
3472 canceledNotifications = new ArrayList<>();
3474 canceledNotifications.add(r);
3478 mNotificationList.remove(i);
3479 cancelNotificationLocked(r, false, reason);
3481 if (doit && canceledNotifications != null) {
3482 final int M = canceledNotifications.size();
3483 for (int i = 0; i < M; i++) {
3484 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3485 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
3488 if (canceledNotifications != null) {
3489 updateLightsLocked();
3491 return canceledNotifications != null;
3495 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
3496 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
3497 String listenerName = listener == null ? null : listener.component.toShortString();
3498 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3499 null, userId, 0, 0, reason, listenerName);
3501 ArrayList<NotificationRecord> canceledNotifications = null;
3502 final int N = mNotificationList.size();
3503 for (int i=N-1; i>=0; i--) {
3504 NotificationRecord r = mNotificationList.get(i);
3505 if (includeCurrentProfiles) {
3506 if (!notificationMatchesCurrentProfiles(r, userId)) {
3510 if (!notificationMatchesUserId(r, userId)) {
3515 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3516 | Notification.FLAG_NO_CLEAR)) == 0) {
3517 mNotificationList.remove(i);
3518 cancelNotificationLocked(r, true, reason);
3519 // Make a note so we can cancel children later.
3520 if (canceledNotifications == null) {
3521 canceledNotifications = new ArrayList<>();
3523 canceledNotifications.add(r);
3526 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3527 for (int i = 0; i < M; i++) {
3528 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3529 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
3531 updateLightsLocked();
3534 // Warning: The caller is responsible for invoking updateLightsLocked().
3535 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3536 String listenerName, int reason, boolean sendDelete) {
3537 Notification n = r.getNotification();
3538 if (!n.isGroupSummary()) {
3542 String pkg = r.sbn.getPackageName();
3543 int userId = r.getUserId();
3546 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3550 final int N = mNotificationList.size();
3551 for (int i = N - 1; i >= 0; i--) {
3552 NotificationRecord childR = mNotificationList.get(i);
3553 StatusBarNotification childSbn = childR.sbn;
3554 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
3555 childR.getGroupKey().equals(r.getGroupKey())) {
3556 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3557 childSbn.getTag(), userId, 0, 0, reason, listenerName);
3558 mNotificationList.remove(i);
3559 cancelNotificationLocked(childR, sendDelete, reason);
3564 // lock on mNotificationList
3565 void updateLightsLocked()
3567 // handle notification lights
3568 NotificationRecord ledNotification = null;
3569 while (ledNotification == null && !mLights.isEmpty()) {
3570 final String owner = mLights.get(mLights.size() - 1);
3571 ledNotification = mNotificationsByKey.get(owner);
3572 if (ledNotification == null) {
3573 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3574 mLights.remove(owner);
3578 // Don't flash while we are in a call or screen is on
3579 if (ledNotification == null || mInCall || mScreenOn) {
3580 mNotificationLight.turnOff();
3581 if (mStatusBar != null) {
3582 mStatusBar.notificationLightOff();
3585 final Notification ledno = ledNotification.sbn.getNotification();
3586 int ledARGB = ledno.ledARGB;
3587 int ledOnMS = ledno.ledOnMS;
3588 int ledOffMS = ledno.ledOffMS;
3589 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3590 ledARGB = mDefaultNotificationColor;
3591 ledOnMS = mDefaultNotificationLedOn;
3592 ledOffMS = mDefaultNotificationLedOff;
3594 if (mNotificationPulseEnabled) {
3596 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3599 if (mStatusBar != null) {
3600 // let SystemUI make an independent decision
3601 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3606 // lock on mNotificationList
3607 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3609 ArrayList<NotificationRecord> list = mNotificationList;
3610 final int len = list.size();
3611 for (int i=0; i<len; i++) {
3612 NotificationRecord r = list.get(i);
3613 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3614 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3621 // lock on mNotificationList
3622 int indexOfNotificationLocked(String key) {
3623 final int N = mNotificationList.size();
3624 for (int i = 0; i < N; i++) {
3625 if (key.equals(mNotificationList.get(i).getKey())) {
3632 private void updateNotificationPulse() {
3633 synchronized (mNotificationList) {
3634 updateLightsLocked();
3638 private static boolean isUidSystem(int uid) {
3639 final int appid = UserHandle.getAppId(uid);
3640 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3643 private static boolean isCallerSystem() {
3644 return isUidSystem(Binder.getCallingUid());
3647 private static void checkCallerIsSystem() {
3648 if (isCallerSystem()) {
3651 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3654 private static void checkCallerIsSystemOrSameApp(String pkg) {
3655 if (isCallerSystem()) {
3658 final int uid = Binder.getCallingUid();
3660 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3661 pkg, 0, UserHandle.getCallingUserId());
3663 throw new SecurityException("Unknown package " + pkg);
3665 if (!UserHandle.isSameApp(ai.uid, uid)) {
3666 throw new SecurityException("Calling uid " + uid + " gave package"
3667 + pkg + " which is owned by uid " + ai.uid);
3669 } catch (RemoteException re) {
3670 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3674 private static String callStateToString(int state) {
3676 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3677 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3678 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3679 default: return "CALL_STATE_UNKNOWN_" + state;
3683 private void listenForCallState() {
3684 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3686 public void onCallStateChanged(int state, String incomingNumber) {
3687 if (mCallState == state) return;
3688 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3691 }, PhoneStateListener.LISTEN_CALL_STATE);
3695 * Generates a NotificationRankingUpdate from 'sbns', considering only
3696 * notifications visible to the given listener.
3698 * <p>Caller must hold a lock on mNotificationList.</p>
3700 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3701 final int N = mNotificationList.size();
3702 ArrayList<String> keys = new ArrayList<String>(N);
3703 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3704 ArrayList<Integer> importance = new ArrayList<>(N);
3705 Bundle overrideGroupKeys = new Bundle();
3706 Bundle visibilityOverrides = new Bundle();
3707 Bundle suppressedVisualEffects = new Bundle();
3708 Bundle explanation = new Bundle();
3709 for (int i = 0; i < N; i++) {
3710 NotificationRecord record = mNotificationList.get(i);
3711 if (!isVisibleToListener(record.sbn, info)) {
3714 final String key = record.sbn.getKey();
3716 importance.add(record.getImportance());
3717 if (record.getImportanceExplanation() != null) {
3718 explanation.putCharSequence(key, record.getImportanceExplanation());
3720 if (record.isIntercepted()) {
3721 interceptedKeys.add(key);
3724 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
3725 if (record.getPackageVisibilityOverride()
3726 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3727 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
3729 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
3731 final int M = keys.size();
3732 String[] keysAr = keys.toArray(new String[M]);
3733 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3734 int[] importanceAr = new int[M];
3735 for (int i = 0; i < M; i++) {
3736 importanceAr[i] = importance.get(i);
3738 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3739 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
3742 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3743 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3746 // TODO: remove this for older listeners.
3750 private boolean isPackageSuspendedForUser(String pkg, int uid) {
3751 int userId = UserHandle.getUserId(uid);
3753 return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
3754 } catch (RemoteException re) {
3755 throw new SecurityException("Could not talk to package manager service");
3756 } catch (IllegalArgumentException ex) {
3757 // Package not found.
3762 private class TrimCache {
3763 StatusBarNotification heavy;
3764 StatusBarNotification sbnClone;
3765 StatusBarNotification sbnCloneLight;
3767 TrimCache(StatusBarNotification sbn) {
3771 StatusBarNotification ForListener(ManagedServiceInfo info) {
3772 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3773 if (sbnCloneLight == null) {
3774 sbnCloneLight = heavy.cloneLight();
3776 return sbnCloneLight;
3778 if (sbnClone == null) {
3779 sbnClone = heavy.clone();
3786 public class NotificationRankers extends ManagedServices {
3788 public NotificationRankers() {
3789 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3793 protected Config getConfig() {
3794 Config c = new Config();
3795 c.caption = "notification ranker service";
3796 c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3797 c.secureSettingName = null;
3798 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
3799 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
3800 c.clientLabel = R.string.notification_ranker_binding_label;
3805 protected IInterface asInterface(IBinder binder) {
3806 return INotificationListener.Stub.asInterface(binder);
3810 protected boolean checkType(IInterface service) {
3811 return service instanceof INotificationListener;
3815 protected void onServiceAdded(ManagedServiceInfo info) {
3816 mListeners.registerGuestService(info);
3820 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3821 mListeners.unregisterService(removed.service, removed.userid);
3824 public void onNotificationEnqueued(final NotificationRecord r) {
3825 final StatusBarNotification sbn = r.sbn;
3826 TrimCache trimCache = new TrimCache(sbn);
3828 // mServices is the list inside ManagedServices of all the rankers,
3829 // There should be only one, but it's a list, so while we enforce
3830 // singularity elsewhere, we keep it general here, to avoid surprises.
3831 for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
3832 boolean sbnVisible = isVisibleToListener(sbn, info);
3837 final int importance = r.getImportance();
3838 final boolean fromUser = r.isImportanceFromUser();
3839 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
3840 mHandler.post(new Runnable() {
3843 notifyEnqueued(info, sbnToPost, importance, fromUser);
3849 private void notifyEnqueued(final ManagedServiceInfo info,
3850 final StatusBarNotification sbn, int importance, boolean fromUser) {
3851 final INotificationListener ranker = (INotificationListener) info.service;
3852 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3854 ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
3855 } catch (RemoteException ex) {
3856 Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
3860 public boolean isEnabled() {
3861 return !mServices.isEmpty();
3865 public void onUserSwitched(int user) {
3866 synchronized (mNotificationList) {
3867 int i = mServices.size()-1;
3869 final ManagedServiceInfo info = mServices.get(i);
3870 unregisterService(info.service, info.userid);
3877 public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
3878 if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
3879 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
3880 if (mRankerServicePackageName == null) {
3884 if (pkgList != null && (pkgList.length > 0)) {
3885 for (String pkgName : pkgList) {
3886 if (mRankerServicePackageName.equals(pkgName)) {
3893 protected void registerRanker() {
3894 // Find the updatable ranker and register it.
3895 if (mRankerServicePackageName == null) {
3896 Slog.w(TAG, "could not start ranker service: no package specified!");
3899 Set<ComponentName> rankerComponents = queryPackageForServices(
3900 mRankerServicePackageName, UserHandle.USER_SYSTEM);
3901 Iterator<ComponentName> iterator = rankerComponents.iterator();
3902 if (iterator.hasNext()) {
3903 ComponentName rankerComponent = iterator.next();
3904 if (iterator.hasNext()) {
3905 Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
3907 registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
3910 Slog.w(TAG, "could not start ranker service: none found");
3915 public class NotificationListeners extends ManagedServices {
3917 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3919 public NotificationListeners() {
3920 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3924 protected Config getConfig() {
3925 Config c = new Config();
3926 c.caption = "notification listener";
3927 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3928 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3929 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3930 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3931 c.clientLabel = R.string.notification_listener_binding_label;
3936 protected IInterface asInterface(IBinder binder) {
3937 return INotificationListener.Stub.asInterface(binder);
3941 protected boolean checkType(IInterface service) {
3942 return service instanceof INotificationListener;
3946 public void onServiceAdded(ManagedServiceInfo info) {
3947 final INotificationListener listener = (INotificationListener) info.service;
3948 final NotificationRankingUpdate update;
3949 synchronized (mNotificationList) {
3950 update = makeRankingUpdateLocked(info);
3953 listener.onListenerConnected(update);
3954 } catch (RemoteException e) {
3960 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3961 if (removeDisabledHints(removed)) {
3962 updateListenerHintsLocked();
3963 updateEffectsSuppressorLocked();
3965 mLightTrimListeners.remove(removed);
3968 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3969 if (trim == TRIM_LIGHT) {
3970 mLightTrimListeners.add(info);
3972 mLightTrimListeners.remove(info);
3976 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
3977 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
3981 * asynchronously notify all listeners about a new notification
3984 * Also takes care of removing a notification that has been visible to a listener before,
3985 * but isn't anymore.
3987 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
3988 // Lazily initialized snapshots of the notification.
3989 TrimCache trimCache = new TrimCache(sbn);
3991 for (final ManagedServiceInfo info : mServices) {
3992 boolean sbnVisible = isVisibleToListener(sbn, info);
3993 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
3994 // This notification hasn't been and still isn't visible -> ignore.
3995 if (!oldSbnVisible && !sbnVisible) {
3998 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
4000 // This notification became invisible -> remove the old one.
4001 if (oldSbnVisible && !sbnVisible) {
4002 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
4003 mHandler.post(new Runnable() {
4006 notifyRemoved(info, oldSbnLightClone, update);
4012 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
4013 mHandler.post(new Runnable() {
4016 notifyPosted(info, sbnToPost, update);
4023 * asynchronously notify all listeners about a removed notification
4025 public void notifyRemovedLocked(StatusBarNotification sbn) {
4026 // make a copy in case changes are made to the underlying Notification object
4027 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
4029 final StatusBarNotification sbnLight = sbn.cloneLight();
4030 for (final ManagedServiceInfo info : mServices) {
4031 if (!isVisibleToListener(sbn, info)) {
4034 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
4035 mHandler.post(new Runnable() {
4038 notifyRemoved(info, sbnLight, update);
4045 * asynchronously notify all listeners about a reordering of notifications
4047 public void notifyRankingUpdateLocked() {
4048 for (final ManagedServiceInfo serviceInfo : mServices) {
4049 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4052 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
4053 mHandler.post(new Runnable() {
4056 notifyRankingUpdate(serviceInfo, update);
4062 public void notifyListenerHintsChangedLocked(final int hints) {
4063 for (final ManagedServiceInfo serviceInfo : mServices) {
4064 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4067 mHandler.post(new Runnable() {
4070 notifyListenerHintsChanged(serviceInfo, hints);
4076 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
4077 for (final ManagedServiceInfo serviceInfo : mServices) {
4078 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4081 mHandler.post(new Runnable() {
4084 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
4090 private void notifyPosted(final ManagedServiceInfo info,
4091 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
4092 final INotificationListener listener = (INotificationListener)info.service;
4093 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4095 listener.onNotificationPosted(sbnHolder, rankingUpdate);
4096 } catch (RemoteException ex) {
4097 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4101 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
4102 NotificationRankingUpdate rankingUpdate) {
4103 if (!info.enabledAndUserMatches(sbn.getUserId())) {
4106 final INotificationListener listener = (INotificationListener) info.service;
4107 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4109 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
4110 } catch (RemoteException ex) {
4111 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
4115 private void notifyRankingUpdate(ManagedServiceInfo info,
4116 NotificationRankingUpdate rankingUpdate) {
4117 final INotificationListener listener = (INotificationListener) info.service;
4119 listener.onNotificationRankingUpdate(rankingUpdate);
4120 } catch (RemoteException ex) {
4121 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4125 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
4126 final INotificationListener listener = (INotificationListener) info.service;
4128 listener.onListenerHintsChanged(hints);
4129 } catch (RemoteException ex) {
4130 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
4134 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4135 int interruptionFilter) {
4136 final INotificationListener listener = (INotificationListener) info.service;
4138 listener.onInterruptionFilterChanged(interruptionFilter);
4139 } catch (RemoteException ex) {
4140 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4144 private boolean isListenerPackage(String packageName) {
4145 if (packageName == null) {
4148 // TODO: clean up locking object later
4149 synchronized (mNotificationList) {
4150 for (final ManagedServiceInfo serviceInfo : mServices) {
4151 if (packageName.equals(serviceInfo.component.getPackageName())) {
4160 public static final class DumpFilter {
4161 public boolean filtered = false;
4162 public String pkgFilter;
4165 public boolean stats;
4166 public boolean redact = true;
4168 public static DumpFilter parseFromArguments(String[] args) {
4169 final DumpFilter filter = new DumpFilter();
4170 for (int ai = 0; ai < args.length; ai++) {
4171 final String a = args[ai];
4172 if ("--noredact".equals(a) || "--reveal".equals(a)) {
4173 filter.redact = false;
4174 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4175 if (ai < args.length-1) {
4177 filter.pkgFilter = args[ai].trim().toLowerCase();
4178 if (filter.pkgFilter.isEmpty()) {
4179 filter.pkgFilter = null;
4181 filter.filtered = true;
4184 } else if ("--zen".equals(a) || "zen".equals(a)) {
4185 filter.filtered = true;
4187 } else if ("--stats".equals(a)) {
4188 filter.stats = true;
4189 if (ai < args.length-1) {
4191 filter.since = Long.valueOf(args[ai]);
4200 public boolean matches(StatusBarNotification sbn) {
4201 if (!filtered) return true;
4202 return zen ? true : sbn != null
4203 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
4206 public boolean matches(ComponentName component) {
4207 if (!filtered) return true;
4208 return zen ? true : component != null && matches(component.getPackageName());
4211 public boolean matches(String pkg) {
4212 if (!filtered) return true;
4213 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
4217 public String toString() {
4218 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
4223 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4224 * binder without sending large amounts of data over a oneway transaction.
4226 private static final class StatusBarNotificationHolder
4227 extends IStatusBarNotificationHolder.Stub {
4228 private StatusBarNotification mValue;
4230 public StatusBarNotificationHolder(StatusBarNotification value) {
4234 /** Get the held value and clear it. This function should only be called once per holder */
4236 public StatusBarNotification get() {
4237 StatusBarNotification value = mValue;
4243 private final class PolicyAccess {
4244 private static final String SEPARATOR = ":";
4245 private final String[] PERM = {
4246 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4249 public boolean isPackageGranted(String pkg) {
4250 return pkg != null && getGrantedPackages().contains(pkg);
4253 public void put(String pkg, boolean granted) {
4254 if (pkg == null) return;
4255 final ArraySet<String> pkgs = getGrantedPackages();
4258 changed = pkgs.add(pkg);
4260 changed = pkgs.remove(pkg);
4262 if (!changed) return;
4263 final String setting = TextUtils.join(SEPARATOR, pkgs);
4264 final int currentUser = ActivityManager.getCurrentUser();
4265 Settings.Secure.putStringForUser(getContext().getContentResolver(),
4266 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4269 getContext().sendBroadcastAsUser(new Intent(NotificationManager
4270 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4272 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4275 public ArraySet<String> getGrantedPackages() {
4276 final ArraySet<String> pkgs = new ArraySet<>();
4278 long identity = Binder.clearCallingIdentity();
4280 final String setting = Settings.Secure.getStringForUser(
4281 getContext().getContentResolver(),
4282 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4283 ActivityManager.getCurrentUser());
4284 if (setting != null) {
4285 final String[] tokens = setting.split(SEPARATOR);
4286 for (int i = 0; i < tokens.length; i++) {
4287 String token = tokens[i];
4288 if (token != null) {
4289 token = token.trim();
4291 if (TextUtils.isEmpty(token)) {
4298 Binder.restoreCallingIdentity(identity);
4303 public String[] getRequestingPackages() throws RemoteException {
4304 final ParceledListSlice list = AppGlobals.getPackageManager()
4305 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4306 ActivityManager.getCurrentUser());
4307 final List<PackageInfo> pkgs = list.getList();
4308 if (pkgs == null || pkgs.isEmpty()) return new String[0];
4309 final int N = pkgs.size();
4310 final String[] rt = new String[N];
4311 for (int i = 0; i < N; i++) {
4312 rt[i] = pkgs.get(i).packageName;