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.StatusBarManager;
62 import android.app.backup.BackupManager;
63 import android.app.usage.UsageEvents;
64 import android.app.usage.UsageStatsManagerInternal;
65 import android.content.BroadcastReceiver;
66 import android.content.ComponentName;
67 import android.content.ContentResolver;
68 import android.content.Context;
69 import android.content.Intent;
70 import android.content.IntentFilter;
71 import android.content.pm.ApplicationInfo;
72 import android.content.pm.IPackageManager;
73 import android.content.pm.PackageInfo;
74 import android.content.pm.PackageManager;
75 import android.content.pm.PackageManager.NameNotFoundException;
76 import android.content.pm.ParceledListSlice;
77 import android.content.pm.UserInfo;
78 import android.content.res.Resources;
79 import android.database.ContentObserver;
80 import android.media.AudioAttributes;
81 import android.media.AudioManager;
82 import android.media.AudioManagerInternal;
83 import android.media.AudioSystem;
84 import android.media.IRingtonePlayer;
85 import android.net.Uri;
86 import android.os.Binder;
87 import android.os.Bundle;
88 import android.os.Environment;
89 import android.os.Handler;
90 import android.os.HandlerThread;
91 import android.os.IBinder;
92 import android.os.IInterface;
93 import android.os.Looper;
94 import android.os.Message;
95 import android.os.Process;
96 import android.os.RemoteException;
97 import android.os.SystemClock;
98 import android.os.SystemProperties;
99 import android.os.UserHandle;
100 import android.os.UserManager;
101 import android.os.Vibrator;
102 import android.provider.Settings;
103 import android.service.notification.Adjustment;
104 import android.service.notification.Condition;
105 import android.service.notification.IConditionProvider;
106 import android.service.notification.INotificationListener;
107 import android.service.notification.IStatusBarNotificationHolder;
108 import android.service.notification.NotificationRankerService;
109 import android.service.notification.NotificationListenerService;
110 import android.service.notification.NotificationRankingUpdate;
111 import android.service.notification.StatusBarNotification;
112 import android.service.notification.ZenModeConfig;
113 import android.telephony.PhoneStateListener;
114 import android.telephony.TelephonyManager;
115 import android.text.TextUtils;
116 import android.util.ArrayMap;
117 import android.util.ArraySet;
118 import android.util.AtomicFile;
119 import android.util.Log;
120 import android.util.Slog;
121 import android.util.SparseArray;
122 import android.util.Xml;
123 import android.view.WindowManager;
124 import android.view.WindowManagerInternal;
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.policy.PhoneWindowManager;
142 import com.android.server.statusbar.StatusBarManagerInternal;
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 = 10f;
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 = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
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 WindowManagerInternal mWindowManagerInternal;
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;
457 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
461 this.callback = callback;
462 this.duration = duration;
466 void update(int duration) {
467 this.duration = duration;
470 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
471 if (filter != null && !filter.matches(pkg)) return;
472 pw.println(prefix + this);
476 public final String toString()
478 return "ToastRecord{"
479 + Integer.toHexString(System.identityHashCode(this))
481 + " callback=" + callback
482 + " duration=" + duration;
486 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
489 public void onSetDisabled(int status) {
490 synchronized (mNotificationList) {
491 mDisableNotificationEffects =
492 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
493 if (disableNotificationEffects(null) != null) {
494 // cancel whatever's going on
495 long identity = Binder.clearCallingIdentity();
497 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
498 if (player != null) {
501 } catch (RemoteException e) {
503 Binder.restoreCallingIdentity(identity);
506 identity = Binder.clearCallingIdentity();
510 Binder.restoreCallingIdentity(identity);
517 public void onClearAll(int callingUid, int callingPid, int userId) {
518 synchronized (mNotificationList) {
519 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
520 /*includeCurrentProfiles*/ true);
525 public void onNotificationClick(int callingUid, int callingPid, String key) {
526 synchronized (mNotificationList) {
527 NotificationRecord r = mNotificationsByKey.get(key);
529 Log.w(TAG, "No notification with key: " + key);
532 final long now = System.currentTimeMillis();
533 EventLogTags.writeNotificationClicked(key,
534 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
536 StatusBarNotification sbn = r.sbn;
537 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
538 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
539 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
540 REASON_DELEGATE_CLICK, null);
545 public void onNotificationActionClick(int callingUid, int callingPid, String key,
547 synchronized (mNotificationList) {
548 NotificationRecord r = mNotificationsByKey.get(key);
550 Log.w(TAG, "No notification with key: " + key);
553 final long now = System.currentTimeMillis();
554 EventLogTags.writeNotificationActionClicked(key, actionIndex,
555 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
556 // TODO: Log action click via UsageStats.
561 public void onNotificationClear(int callingUid, int callingPid,
562 String pkg, String tag, int id, int userId) {
563 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
564 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
565 true, userId, REASON_DELEGATE_CANCEL, null);
569 public void onPanelRevealed(boolean clearEffects, int items) {
570 EventLogTags.writeNotificationPanelRevealed(items);
577 public void onPanelHidden() {
578 EventLogTags.writeNotificationPanelHidden();
582 public void clearEffects() {
583 synchronized (mNotificationList) {
584 if (DBG) Slog.d(TAG, "clearEffects");
586 clearVibrateLocked();
592 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
593 int uid, int initialPid, String message, int userId) {
594 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
595 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
596 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
597 REASON_DELEGATE_ERROR, null);
598 long ident = Binder.clearCallingIdentity();
600 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
601 "Bad notification posted from package " + pkg
603 } catch (RemoteException e) {
605 Binder.restoreCallingIdentity(ident);
609 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
610 NotificationVisibility[] noLongerVisibleKeys) {
611 synchronized (mNotificationList) {
612 for (NotificationVisibility nv : newlyVisibleKeys) {
613 NotificationRecord r = mNotificationsByKey.get(nv.key);
614 if (r == null) continue;
615 r.setVisibility(true, nv.rank);
618 // Note that we might receive this event after notifications
619 // have already left the system, e.g. after dismissing from the
620 // shade. Hence not finding notifications in
621 // mNotificationsByKey is not an exceptional condition.
622 for (NotificationVisibility nv : noLongerVisibleKeys) {
623 NotificationRecord r = mNotificationsByKey.get(nv.key);
624 if (r == null) continue;
625 r.setVisibility(false, nv.rank);
632 public void onNotificationExpansionChanged(String key,
633 boolean userAction, boolean expanded) {
634 synchronized (mNotificationList) {
635 NotificationRecord r = mNotificationsByKey.get(key);
637 r.stats.onExpansionChanged(userAction, expanded);
638 final long now = System.currentTimeMillis();
639 EventLogTags.writeNotificationExpansion(key,
640 userAction ? 1 : 0, expanded ? 1 : 0,
641 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
647 private void clearSoundLocked() {
648 mSoundNotificationKey = null;
649 long identity = Binder.clearCallingIdentity();
651 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
652 if (player != null) {
655 } catch (RemoteException e) {
657 Binder.restoreCallingIdentity(identity);
661 private void clearVibrateLocked() {
662 mVibrateNotificationKey = null;
663 long identity = Binder.clearCallingIdentity();
667 Binder.restoreCallingIdentity(identity);
671 private void clearLightsLocked() {
674 updateLightsLocked();
677 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
679 public void onReceive(Context context, Intent intent) {
680 String action = intent.getAction();
681 if (action == null) {
685 boolean queryRestart = false;
686 boolean queryRemove = false;
687 boolean packageChanged = false;
688 boolean cancelNotifications = true;
689 int reason = REASON_PACKAGE_CHANGED;
691 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
692 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
693 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
694 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
695 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
696 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
697 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
698 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
699 UserHandle.USER_ALL);
700 String pkgList[] = null;
701 boolean removingPackage = queryRemove &&
702 !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
703 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
704 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
705 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
706 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
707 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
708 reason = REASON_PACKAGE_SUSPENDED;
709 } else if (queryRestart) {
710 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
712 Uri uri = intent.getData();
716 String pkgName = uri.getSchemeSpecificPart();
717 if (pkgName == null) {
720 if (packageChanged) {
721 // We cancel notifications for packages which have just been disabled
723 final IPackageManager pm = AppGlobals.getPackageManager();
724 final int enabled = pm.getApplicationEnabledSetting(pkgName,
725 changeUserId != UserHandle.USER_ALL ? changeUserId :
726 UserHandle.USER_SYSTEM);
727 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
728 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
729 cancelNotifications = false;
731 } catch (IllegalArgumentException e) {
732 // Package doesn't exist; probably racing with uninstall.
733 // cancelNotifications is already true, so nothing to do here.
735 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
737 } catch (RemoteException e) {
738 // Failed to talk to PackageManagerService Should never happen!
741 pkgList = new String[]{pkgName};
744 if (pkgList != null && (pkgList.length > 0)) {
745 for (String pkgName : pkgList) {
746 if (cancelNotifications) {
747 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
748 changeUserId, reason, null);
752 mListeners.onPackagesChanged(removingPackage, pkgList);
753 mRankerServices.onPackagesChanged(removingPackage, pkgList);
754 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
755 mRankingHelper.onPackagesChanged(removingPackage, pkgList);
760 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
762 public void onReceive(Context context, Intent intent) {
763 String action = intent.getAction();
765 if (action.equals(Intent.ACTION_SCREEN_ON)) {
766 // Keep track of screen on/off state, but do not turn off the notification light
767 // until user passes through the lock screen or views the notification.
769 updateNotificationPulse();
770 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
772 updateNotificationPulse();
773 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
774 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
775 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
776 updateNotificationPulse();
777 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
778 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
779 if (userHandle >= 0) {
780 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
781 REASON_USER_STOPPED, null);
783 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
784 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
785 if (userHandle >= 0) {
786 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
787 REASON_PROFILE_TURNED_OFF, null);
789 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
790 // turn off LED when user passes through lock screen
791 mNotificationLight.turnOff();
792 if (mStatusBar != null) {
793 mStatusBar.notificationLightOff();
795 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
796 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
797 // reload per-user settings
798 mSettingsObserver.update(null);
799 mUserProfiles.updateCache(context);
800 // Refresh managed services
801 mConditionProviders.onUserSwitched(user);
802 mListeners.onUserSwitched(user);
803 mRankerServices.onUserSwitched(user);
804 mZenModeHelper.onUserSwitched(user);
805 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
806 mUserProfiles.updateCache(context);
807 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
808 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
809 mZenModeHelper.onUserRemoved(user);
810 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
811 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
812 mConditionProviders.onUserUnlocked(user);
813 mListeners.onUserUnlocked(user);
814 mRankerServices.onUserUnlocked(user);
815 mZenModeHelper.onUserUnlocked(user);
820 private final class SettingsObserver extends ContentObserver {
821 private final Uri NOTIFICATION_LIGHT_PULSE_URI
822 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
823 private final Uri NOTIFICATION_SOUND_URI
824 = Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND);
825 private final Uri NOTIFICATION_RATE_LIMIT_URI
826 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
828 SettingsObserver(Handler handler) {
833 ContentResolver resolver = getContext().getContentResolver();
834 resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
835 false, this, UserHandle.USER_ALL);
836 resolver.registerContentObserver(NOTIFICATION_SOUND_URI,
837 false, this, UserHandle.USER_ALL);
838 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
839 false, this, UserHandle.USER_ALL);
843 @Override public void onChange(boolean selfChange, Uri uri) {
847 public void update(Uri uri) {
848 ContentResolver resolver = getContext().getContentResolver();
849 if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
850 boolean pulseEnabled = Settings.System.getInt(resolver,
851 Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
852 if (mNotificationPulseEnabled != pulseEnabled) {
853 mNotificationPulseEnabled = pulseEnabled;
854 updateNotificationPulse();
857 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
858 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
859 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
861 if (uri == null || NOTIFICATION_SOUND_URI.equals(uri)) {
862 mSystemNotificationSound = Settings.System.getString(resolver,
863 Settings.System.NOTIFICATION_SOUND);
868 private SettingsObserver mSettingsObserver;
869 private ZenModeHelper mZenModeHelper;
871 private final Runnable mBuzzBeepBlinked = new Runnable() {
874 if (mStatusBar != null) {
875 mStatusBar.buzzBeepBlinked();
880 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
881 int[] ar = r.getIntArray(resid);
885 final int len = ar.length > maxlen ? maxlen : ar.length;
886 long[] out = new long[len];
887 for (int i=0; i<len; i++) {
893 public NotificationManagerService(Context context) {
898 void setAudioManager(AudioManager audioMananger) {
899 mAudioManager = audioMananger;
903 void setVibrator(Vibrator vibrator) {
904 mVibrator = vibrator;
908 void setSystemReady(boolean systemReady) {
909 mSystemReady = systemReady;
913 void setHandler(Handler handler) {
918 void setSystemNotificationSound(String systemNotificationSound) {
919 mSystemNotificationSound = systemNotificationSound;
923 public void onStart() {
924 Resources resources = getContext().getResources();
926 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
927 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
928 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
930 mAm = ActivityManagerNative.getDefault();
931 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
932 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
933 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
935 // This is the package that contains the AOSP framework update.
936 mRankerServicePackageName = getContext().getPackageManager()
937 .getServicesSystemSharedLibraryPackageName();
939 mHandler = new WorkerHandler();
940 mRankingThread.start();
941 String[] extractorNames;
943 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
944 } catch (Resources.NotFoundException e) {
945 extractorNames = new String[0];
947 mUsageStats = new NotificationUsageStats(getContext());
948 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
949 mRankingHelper = new RankingHelper(getContext(),
953 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
954 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
955 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
957 public void onConfigChanged() {
962 void onZenModeChanged() {
963 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
964 getContext().sendBroadcastAsUser(
965 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
966 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
967 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
968 synchronized(mNotificationList) {
969 updateInterruptionFilterLocked();
974 void onPolicyChanged() {
975 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
978 final File systemDir = new File(Environment.getDataDirectory(), "system");
979 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
983 // This is a MangedServices object that keeps track of the listeners.
984 mListeners = new NotificationListeners();
986 // This is a MangedServices object that keeps track of the ranker.
987 mRankerServices = new NotificationRankers();
988 // Find the updatable ranker and register it.
989 mRankerServices.registerRanker();
991 mStatusBar = getLocalService(StatusBarManagerInternal.class);
992 if (mStatusBar != null) {
993 mStatusBar.setNotificationDelegate(mNotificationDelegate);
996 final LightsManager lights = getLocalService(LightsManager.class);
997 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
998 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
1000 mDefaultNotificationColor = resources.getColor(
1001 R.color.config_defaultNotificationColor);
1002 mDefaultNotificationLedOn = resources.getInteger(
1003 R.integer.config_defaultNotificationLedOn);
1004 mDefaultNotificationLedOff = resources.getInteger(
1005 R.integer.config_defaultNotificationLedOff);
1007 mDefaultVibrationPattern = getLongArray(resources,
1008 R.array.config_defaultNotificationVibePattern,
1009 VIBRATE_PATTERN_MAXLEN,
1010 DEFAULT_VIBRATE_PATTERN);
1012 mFallbackVibrationPattern = getLongArray(resources,
1013 R.array.config_notificationFallbackVibePattern,
1014 VIBRATE_PATTERN_MAXLEN,
1015 DEFAULT_VIBRATE_PATTERN);
1017 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1019 // Don't start allowing notifications until the setup wizard has run once.
1020 // After that, including subsequent boots, init with notifications turned on.
1021 // This works on the first boot because the setup wizard will toggle this
1022 // flag at least once and we'll go back to 0 after that.
1023 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1024 Settings.Global.DEVICE_PROVISIONED, 0)) {
1025 mDisableNotificationEffects = true;
1027 mZenModeHelper.initZenMode();
1028 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1030 mUserProfiles.updateCache(getContext());
1031 listenForCallState();
1033 // register for various Intents
1034 IntentFilter filter = new IntentFilter();
1035 filter.addAction(Intent.ACTION_SCREEN_ON);
1036 filter.addAction(Intent.ACTION_SCREEN_OFF);
1037 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1038 filter.addAction(Intent.ACTION_USER_PRESENT);
1039 filter.addAction(Intent.ACTION_USER_STOPPED);
1040 filter.addAction(Intent.ACTION_USER_SWITCHED);
1041 filter.addAction(Intent.ACTION_USER_ADDED);
1042 filter.addAction(Intent.ACTION_USER_REMOVED);
1043 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1044 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1045 getContext().registerReceiver(mIntentReceiver, filter);
1047 IntentFilter pkgFilter = new IntentFilter();
1048 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1049 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1050 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1051 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1052 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1053 pkgFilter.addDataScheme("package");
1054 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1057 IntentFilter suspendedPkgFilter = new IntentFilter();
1058 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1059 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1060 suspendedPkgFilter, null, null);
1062 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1063 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1066 mSettingsObserver = new SettingsObserver(mHandler);
1068 mArchive = new Archive(resources.getInteger(
1069 R.integer.config_notificationServiceArchiveSize));
1071 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1072 publishLocalService(NotificationManagerInternal.class, mInternalService);
1075 private void sendRegisteredOnlyBroadcast(String action) {
1076 getContext().sendBroadcastAsUser(new Intent(action)
1077 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1081 * Make sure the XML config and the the AppOps system agree about blocks.
1083 private void syncBlockDb() {
1086 // sync bans from ranker into app opps
1087 Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1088 for(Entry<Integer, String> ban : packageBans.entrySet()) {
1089 final int uid = ban.getKey();
1090 final String packageName = ban.getValue();
1091 setNotificationsEnabledForPackageImpl(packageName, uid, false);
1094 // sync bans from app opps into ranker
1095 packageBans.clear();
1096 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1097 final int userId = user.getUserHandle().getIdentifier();
1098 final PackageManager packageManager = getContext().getPackageManager();
1099 List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1100 final int packageCount = packages.size();
1101 for (int p = 0; p < packageCount; p++) {
1102 final String packageName = packages.get(p).packageName;
1104 final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1105 if (!checkNotificationOp(packageName, uid)) {
1106 packageBans.put(uid, packageName);
1108 } catch (NameNotFoundException e) {
1113 for (Entry<Integer, String> ban : packageBans.entrySet()) {
1114 mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1121 public void onBootPhase(int phase) {
1122 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1123 // no beeping until we're basically done booting
1124 mSystemReady = true;
1126 // Grab our optional AudioService
1127 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1128 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1129 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
1130 mZenModeHelper.onSystemReady();
1131 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1132 // This observer will force an update when observe is called, causing us to
1133 // bind to listener services.
1134 mSettingsObserver.observe();
1135 mListeners.onBootPhaseAppsCanStart();
1136 mRankerServices.onBootPhaseAppsCanStart();
1137 mConditionProviders.onBootPhaseAppsCanStart();
1141 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1142 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1144 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1145 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1147 // Now, cancel any outstanding notifications that are part of a just-disabled app
1148 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1149 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1150 REASON_PACKAGE_BANNED, null);
1154 private void updateListenerHintsLocked() {
1155 final int hints = calculateHints();
1156 if (hints == mListenerHints) return;
1157 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1158 mListenerHints = hints;
1159 scheduleListenerHintsChanged(hints);
1162 private void updateEffectsSuppressorLocked() {
1163 final long updatedSuppressedEffects = calculateSuppressedEffects();
1164 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1165 final List<ComponentName> suppressors = getSuppressors();
1166 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1167 mEffectsSuppressors = suppressors;
1168 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1169 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1172 private ArrayList<ComponentName> getSuppressors() {
1173 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1174 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1175 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1177 for (ManagedServiceInfo info : serviceInfoList) {
1178 names.add(info.component);
1185 private boolean removeDisabledHints(ManagedServiceInfo info) {
1186 return removeDisabledHints(info, 0);
1189 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1190 boolean removed = false;
1192 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1193 final int hint = mListenersDisablingEffects.keyAt(i);
1194 final ArraySet<ManagedServiceInfo> listeners =
1195 mListenersDisablingEffects.valueAt(i);
1197 if (hints == 0 || (hint & hints) == hint) {
1198 removed = removed || listeners.remove(info);
1205 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1206 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1207 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1210 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1211 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1214 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1215 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1219 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1220 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1221 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1224 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1225 hintListeners.add(info);
1228 private int calculateHints() {
1230 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1231 int hint = mListenersDisablingEffects.keyAt(i);
1232 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1234 if (!serviceInfoList.isEmpty()) {
1242 private long calculateSuppressedEffects() {
1243 int hints = calculateHints();
1244 long suppressedEffects = 0;
1246 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1247 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1250 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1251 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1254 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1255 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1258 return suppressedEffects;
1261 private void updateInterruptionFilterLocked() {
1262 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1263 if (interruptionFilter == mInterruptionFilter) return;
1264 mInterruptionFilter = interruptionFilter;
1265 scheduleInterruptionFilterChanged(interruptionFilter);
1268 private final IBinder mService = new INotificationManager.Stub() {
1270 // ============================================================================
1273 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1276 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1277 + " duration=" + duration);
1280 if (pkg == null || callback == null) {
1281 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1285 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1286 final boolean isPackageSuspended =
1287 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1289 if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
1290 || isPackageSuspended)) {
1291 if (!isSystemToast) {
1292 Slog.e(TAG, "Suppressing toast from package " + pkg
1293 + (isPackageSuspended
1294 ? " due to package suspended by administrator."
1295 : " by user request."));
1300 synchronized (mToastQueue) {
1301 int callingPid = Binder.getCallingPid();
1302 long callingId = Binder.clearCallingIdentity();
1305 int index = indexOfToastLocked(pkg, callback);
1306 // If it's already in the queue, we update it in place, we don't
1307 // move it to the end of the queue.
1309 record = mToastQueue.get(index);
1310 record.update(duration);
1312 // Limit the number of toasts that any given package except the android
1313 // package can enqueue. Prevents DOS attacks and deals with leaks.
1314 if (!isSystemToast) {
1316 final int N = mToastQueue.size();
1317 for (int i=0; i<N; i++) {
1318 final ToastRecord r = mToastQueue.get(i);
1319 if (r.pkg.equals(pkg)) {
1321 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1322 Slog.e(TAG, "Package has already posted " + count
1323 + " toasts. Not showing more. Package=" + pkg);
1330 Binder token = new Binder();
1331 mWindowManagerInternal.addWindowToken(token,
1332 WindowManager.LayoutParams.TYPE_TOAST);
1333 record = new ToastRecord(callingPid, pkg, callback, duration, token);
1334 mToastQueue.add(record);
1335 index = mToastQueue.size() - 1;
1336 keepProcessAliveIfNeededLocked(callingPid);
1338 // If it's at index 0, it's the current toast. It doesn't matter if it's
1339 // new or just been updated. Call back and tell it to show itself.
1340 // If the callback fails, this will remove it from the list, so don't
1341 // assume that it's valid after this.
1343 showNextToastLocked();
1346 Binder.restoreCallingIdentity(callingId);
1352 public void cancelToast(String pkg, ITransientNotification callback) {
1353 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1355 if (pkg == null || callback == null) {
1356 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1360 synchronized (mToastQueue) {
1361 long callingId = Binder.clearCallingIdentity();
1363 int index = indexOfToastLocked(pkg, callback);
1365 cancelToastLocked(index);
1367 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1368 + " callback=" + callback);
1371 Binder.restoreCallingIdentity(callingId);
1377 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1378 Notification notification, int[] idOut, int userId) throws RemoteException {
1379 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1380 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1384 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1385 checkCallerIsSystemOrSameApp(pkg);
1386 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1387 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1388 // Don't allow client applications to cancel foreground service notis or autobundled
1390 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1391 (Binder.getCallingUid() == Process.SYSTEM_UID
1392 ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
1393 | (Binder.getCallingUid() == Process.SYSTEM_UID
1394 ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
1395 REASON_APP_CANCEL, null);
1399 public void cancelAllNotifications(String pkg, int userId) {
1400 checkCallerIsSystemOrSameApp(pkg);
1402 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1403 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1405 // Calling from user space, don't allow the canceling of actively
1406 // running foreground services.
1407 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1408 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1409 REASON_APP_CANCEL_ALL, null);
1413 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1414 checkCallerIsSystem();
1416 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1417 mRankingHelper.setEnabled(pkg, uid, enabled);
1422 * Use this when you just want to know if notifications are OK for this package.
1425 public boolean areNotificationsEnabled(String pkg) {
1426 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1430 * Use this when you just want to know if notifications are OK for this package.
1433 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1434 checkCallerIsSystemOrSameApp(pkg);
1435 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1436 == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
1440 public void setPriority(String pkg, int uid, int priority) {
1441 checkCallerIsSystem();
1442 mRankingHelper.setPriority(pkg, uid, priority);
1447 public int getPriority(String pkg, int uid) {
1448 checkCallerIsSystem();
1449 return mRankingHelper.getPriority(pkg, uid);
1453 public void setVisibilityOverride(String pkg, int uid, int visibility) {
1454 checkCallerIsSystem();
1455 mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
1460 public int getVisibilityOverride(String pkg, int uid) {
1461 checkCallerIsSystem();
1462 return mRankingHelper.getVisibilityOverride(pkg, uid);
1466 public void setImportance(String pkg, int uid, int importance) {
1467 enforceSystemOrSystemUI("Caller not system or systemui");
1468 setNotificationsEnabledForPackageImpl(pkg, uid,
1469 importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1470 mRankingHelper.setImportance(pkg, uid, importance);
1475 public int getPackageImportance(String pkg) {
1476 checkCallerIsSystemOrSameApp(pkg);
1477 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1481 public int getImportance(String pkg, int uid) {
1482 enforceSystemOrSystemUI("Caller not system or systemui");
1483 return mRankingHelper.getImportance(pkg, uid);
1487 * System-only API for getting a list of current (i.e. not cleared) notifications.
1489 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1490 * @returns A list of all the notifications, in natural order.
1493 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1494 // enforce() will ensure the calling uid has the correct permission
1495 getContext().enforceCallingOrSelfPermission(
1496 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1497 "NotificationManagerService.getActiveNotifications");
1499 StatusBarNotification[] tmp = null;
1500 int uid = Binder.getCallingUid();
1502 // noteOp will check to make sure the callingPkg matches the uid
1503 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1504 == AppOpsManager.MODE_ALLOWED) {
1505 synchronized (mNotificationList) {
1506 tmp = new StatusBarNotification[mNotificationList.size()];
1507 final int N = mNotificationList.size();
1508 for (int i=0; i<N; i++) {
1509 tmp[i] = mNotificationList.get(i).sbn;
1517 * Public API for getting a list of current notifications for the calling package/uid.
1519 * @returns A list of all the package's notifications, in natural order.
1522 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1523 int incomingUserId) {
1524 checkCallerIsSystemOrSameApp(pkg);
1525 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1526 Binder.getCallingUid(), incomingUserId, true, false,
1527 "getAppActiveNotifications", pkg);
1529 final ArrayList<StatusBarNotification> list
1530 = new ArrayList<StatusBarNotification>(mNotificationList.size());
1532 synchronized (mNotificationList) {
1533 final int N = mNotificationList.size();
1534 for (int i = 0; i < N; i++) {
1535 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1536 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1537 && (sbn.getNotification().flags
1538 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1539 // We could pass back a cloneLight() but clients might get confused and
1540 // try to send this thing back to notify() again, which would not work
1542 final StatusBarNotification sbnOut = new StatusBarNotification(
1543 sbn.getPackageName(),
1545 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1546 0, // hide score from apps
1547 sbn.getNotification().clone(),
1548 sbn.getUser(), sbn.getPostTime());
1554 return new ParceledListSlice<StatusBarNotification>(list);
1558 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1560 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1563 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1564 // enforce() will ensure the calling uid has the correct permission
1565 getContext().enforceCallingOrSelfPermission(
1566 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1567 "NotificationManagerService.getHistoricalNotifications");
1569 StatusBarNotification[] tmp = null;
1570 int uid = Binder.getCallingUid();
1572 // noteOp will check to make sure the callingPkg matches the uid
1573 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1574 == AppOpsManager.MODE_ALLOWED) {
1575 synchronized (mArchive) {
1576 tmp = mArchive.getArray(count);
1583 * Register a listener binder directly with the notification manager.
1585 * Only works with system callers. Apps should extend
1586 * {@link android.service.notification.NotificationListenerService}.
1589 public void registerListener(final INotificationListener listener,
1590 final ComponentName component, final int userid) {
1591 enforceSystemOrSystemUI("INotificationManager.registerListener");
1592 mListeners.registerService(listener, component, userid);
1596 * Remove a listener binder directly
1599 public void unregisterListener(INotificationListener token, int userid) {
1600 mListeners.unregisterService(token, userid);
1604 * Allow an INotificationListener to simulate a "clear all" operation.
1606 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1608 * @param token The binder for the listener, to check that the caller is allowed
1611 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1612 final int callingUid = Binder.getCallingUid();
1613 final int callingPid = Binder.getCallingPid();
1614 long identity = Binder.clearCallingIdentity();
1616 synchronized (mNotificationList) {
1617 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1619 final int N = keys.length;
1620 for (int i = 0; i < N; i++) {
1621 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1622 if (r == null) continue;
1623 final int userId = r.sbn.getUserId();
1624 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1625 !mUserProfiles.isCurrentProfile(userId)) {
1626 throw new SecurityException("Disallowed call from listener: "
1629 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1630 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1634 cancelAllLocked(callingUid, callingPid, info.userid,
1635 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1639 Binder.restoreCallingIdentity(identity);
1644 * Handle request from an approved listener to re-enable itself.
1646 * @param component The componenet to be re-enabled, caller must match package.
1649 public void requestBindListener(ComponentName component) {
1650 checkCallerIsSystemOrSameApp(component.getPackageName());
1651 long identity = Binder.clearCallingIdentity();
1653 ManagedServices manager =
1654 mRankerServices.isComponentEnabledForCurrentProfiles(component)
1657 manager.setComponentState(component, true);
1659 Binder.restoreCallingIdentity(identity);
1664 public void requestUnbindListener(INotificationListener token) {
1665 long identity = Binder.clearCallingIdentity();
1667 // allow bound services to disable themselves
1668 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1669 info.getOwner().setComponentState(info.component, false);
1671 Binder.restoreCallingIdentity(identity);
1676 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1677 long identity = Binder.clearCallingIdentity();
1679 synchronized (mNotificationList) {
1680 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1682 final int N = keys.length;
1683 for (int i = 0; i < N; i++) {
1684 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1685 if (r == null) continue;
1686 final int userId = r.sbn.getUserId();
1687 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1688 !mUserProfiles.isCurrentProfile(userId)) {
1689 throw new SecurityException("Disallowed call from listener: "
1693 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1694 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1695 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
1697 UsageEvents.Event.USER_INTERACTION);
1704 Binder.restoreCallingIdentity(identity);
1708 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1709 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1710 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1711 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1713 userId, REASON_LISTENER_CANCEL, info);
1717 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1719 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1721 * @param token The binder for the listener, to check that the caller is allowed
1724 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1725 String tag, int id) {
1726 final int callingUid = Binder.getCallingUid();
1727 final int callingPid = Binder.getCallingPid();
1728 long identity = Binder.clearCallingIdentity();
1730 synchronized (mNotificationList) {
1731 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1732 if (info.supportsProfiles()) {
1733 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1734 + "from " + info.component
1735 + " use cancelNotification(key) instead.");
1737 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1738 pkg, tag, id, info.userid);
1742 Binder.restoreCallingIdentity(identity);
1747 * Allow an INotificationListener to request the list of outstanding notifications seen by
1748 * the current user. Useful when starting up, after which point the listener callbacks
1751 * @param token The binder for the listener, to check that the caller is allowed
1752 * @param keys An array of notification keys to fetch, or null to fetch everything
1753 * @returns The return value will contain the notifications specified in keys, in that
1754 * order, or if keys is null, all the notifications, in natural order.
1757 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1758 INotificationListener token, String[] keys, int trim) {
1759 synchronized (mNotificationList) {
1760 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1761 final boolean getKeys = keys != null;
1762 final int N = getKeys ? keys.length : mNotificationList.size();
1763 final ArrayList<StatusBarNotification> list
1764 = new ArrayList<StatusBarNotification>(N);
1765 for (int i=0; i<N; i++) {
1766 final NotificationRecord r = getKeys
1767 ? mNotificationsByKey.get(keys[i])
1768 : mNotificationList.get(i);
1769 if (r == null) continue;
1770 StatusBarNotification sbn = r.sbn;
1771 if (!isVisibleToListener(sbn, info)) continue;
1772 StatusBarNotification sbnToSend =
1773 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1774 list.add(sbnToSend);
1776 return new ParceledListSlice<StatusBarNotification>(list);
1781 public void requestHintsFromListener(INotificationListener token, int hints) {
1782 final long identity = Binder.clearCallingIdentity();
1784 synchronized (mNotificationList) {
1785 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1786 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
1787 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
1788 | HINT_HOST_DISABLE_CALL_EFFECTS;
1789 final boolean disableEffects = (hints & disableEffectsMask) != 0;
1790 if (disableEffects) {
1791 addDisabledHints(info, hints);
1793 removeDisabledHints(info, hints);
1795 updateListenerHintsLocked();
1796 updateEffectsSuppressorLocked();
1799 Binder.restoreCallingIdentity(identity);
1804 public int getHintsFromListener(INotificationListener token) {
1805 synchronized (mNotificationList) {
1806 return mListenerHints;
1811 public void requestInterruptionFilterFromListener(INotificationListener token,
1812 int interruptionFilter) throws RemoteException {
1813 final long identity = Binder.clearCallingIdentity();
1815 synchronized (mNotificationList) {
1816 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1817 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1818 updateInterruptionFilterLocked();
1821 Binder.restoreCallingIdentity(identity);
1826 public int getInterruptionFilterFromListener(INotificationListener token)
1827 throws RemoteException {
1828 synchronized (mNotificationLight) {
1829 return mInterruptionFilter;
1834 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1835 throws RemoteException {
1836 synchronized (mNotificationList) {
1837 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1838 if (info == null) return;
1839 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1844 public int getZenMode() {
1845 return mZenModeHelper.getZenMode();
1849 public ZenModeConfig getZenModeConfig() {
1850 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1851 return mZenModeHelper.getConfig();
1855 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1856 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1857 final long identity = Binder.clearCallingIdentity();
1859 mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
1861 Binder.restoreCallingIdentity(identity);
1866 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
1867 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
1868 return mZenModeHelper.getZenRules();
1872 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
1873 Preconditions.checkNotNull(id, "Id is null");
1874 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
1875 return mZenModeHelper.getAutomaticZenRule(id);
1879 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
1880 throws RemoteException {
1881 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1882 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1883 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1884 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1885 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
1887 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
1888 "addAutomaticZenRule");
1892 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
1893 throws RemoteException {
1894 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
1895 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
1896 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
1897 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
1898 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
1900 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
1901 "updateAutomaticZenRule");
1905 public boolean removeAutomaticZenRule(String id) throws RemoteException {
1906 Preconditions.checkNotNull(id, "Id is null");
1907 // Verify that they can modify zen rules.
1908 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
1910 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
1914 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
1915 Preconditions.checkNotNull(packageName, "Package name is null");
1916 enforceSystemOrSystemUI("removeAutomaticZenRules");
1918 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
1922 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
1923 Preconditions.checkNotNull(owner, "Owner is null");
1924 enforceSystemOrSystemUI("getRuleInstanceCount");
1926 return mZenModeHelper.getCurrentInstanceCount(owner);
1930 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
1931 enforcePolicyAccess(pkg, "setInterruptionFilter");
1932 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
1933 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
1934 final long identity = Binder.clearCallingIdentity();
1936 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
1938 Binder.restoreCallingIdentity(identity);
1943 public void notifyConditions(final String pkg, IConditionProvider provider,
1944 final Condition[] conditions) {
1945 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
1946 checkCallerIsSystemOrSameApp(pkg);
1947 mHandler.post(new Runnable() {
1950 mConditionProviders.notifyConditions(pkg, info, conditions);
1955 private void enforceSystemOrSystemUIOrVolume(String message) {
1956 if (mAudioManagerInternal != null) {
1957 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
1958 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
1962 enforceSystemOrSystemUI(message);
1965 private void enforceSystemOrSystemUI(String message) {
1966 if (isCallerSystem()) return;
1967 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
1971 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
1973 checkCallerIsSystemOrSameApp(pkg);
1974 } catch (SecurityException e) {
1975 getContext().enforceCallingPermission(
1976 android.Manifest.permission.STATUS_BAR_SERVICE,
1981 private void enforcePolicyAccess(int uid, String method) {
1982 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
1983 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
1986 boolean accessAllowed = false;
1987 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
1988 final int packageCount = packages.length;
1989 for (int i = 0; i < packageCount; i++) {
1990 if (checkPolicyAccess(packages[i])) {
1991 accessAllowed = true;
1994 if (!accessAllowed) {
1995 Slog.w(TAG, "Notification policy access denied calling " + method);
1996 throw new SecurityException("Notification policy access denied");
2000 private void enforcePolicyAccess(String pkg, String method) {
2001 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2002 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2005 checkCallerIsSameApp(pkg);
2006 if (!checkPolicyAccess(pkg)) {
2007 Slog.w(TAG, "Notification policy access denied calling " + method);
2008 throw new SecurityException("Notification policy access denied");
2012 private boolean checkPackagePolicyAccess(String pkg) {
2013 return mPolicyAccess.isPackageGranted(pkg);
2016 private boolean checkPolicyAccess(String pkg) {
2018 int uid = getContext().getPackageManager().getPackageUidAsUser(
2019 pkg, UserHandle.getCallingUserId());
2020 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2021 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2025 } catch (NameNotFoundException e) {
2028 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2032 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2033 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2034 != PackageManager.PERMISSION_GRANTED) {
2035 pw.println("Permission Denial: can't dump NotificationManager from pid="
2036 + Binder.getCallingPid()
2037 + ", uid=" + Binder.getCallingUid());
2041 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2042 if (filter != null && filter.stats) {
2043 dumpJson(pw, filter);
2045 dumpImpl(pw, filter);
2050 public ComponentName getEffectsSuppressor() {
2051 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
2052 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2056 public boolean matchesCallFilter(Bundle extras) {
2057 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2058 return mZenModeHelper.matchesCallFilter(
2059 Binder.getCallingUserHandle(),
2061 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2062 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2063 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2067 public boolean isSystemConditionProviderEnabled(String path) {
2068 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
2069 return mConditionProviders.isSystemProviderEnabled(path);
2072 // Backup/restore interface
2074 public byte[] getBackupPayload(int user) {
2075 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2076 //TODO: http://b/22388012
2077 if (user != UserHandle.USER_SYSTEM) {
2078 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2081 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2083 writePolicyXml(baos, true /*forBackup*/);
2084 return baos.toByteArray();
2085 } catch (IOException e) {
2086 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2092 public void applyRestore(byte[] payload, int user) {
2093 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2094 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2095 if (payload == null) {
2096 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2099 //TODO: http://b/22388012
2100 if (user != UserHandle.USER_SYSTEM) {
2101 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2104 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2106 readPolicyXml(bais, true /*forRestore*/);
2108 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2109 Slog.w(TAG, "applyRestore: error reading payload", e);
2114 public boolean isNotificationPolicyAccessGranted(String pkg) {
2115 return checkPolicyAccess(pkg);
2119 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2120 enforceSystemOrSystemUIOrSamePackage(pkg,
2121 "request policy access status for another package");
2122 return checkPolicyAccess(pkg);
2126 public String[] getPackagesRequestingNotificationPolicyAccess()
2127 throws RemoteException {
2128 enforceSystemOrSystemUI("request policy access packages");
2129 final long identity = Binder.clearCallingIdentity();
2131 return mPolicyAccess.getRequestingPackages();
2133 Binder.restoreCallingIdentity(identity);
2138 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2139 throws RemoteException {
2140 enforceSystemOrSystemUI("grant notification policy access");
2141 final long identity = Binder.clearCallingIdentity();
2143 synchronized (mNotificationList) {
2144 mPolicyAccess.put(pkg, granted);
2147 Binder.restoreCallingIdentity(identity);
2152 public Policy getNotificationPolicy(String pkg) {
2153 enforcePolicyAccess(pkg, "getNotificationPolicy");
2154 final long identity = Binder.clearCallingIdentity();
2156 return mZenModeHelper.getNotificationPolicy();
2158 Binder.restoreCallingIdentity(identity);
2163 public void setNotificationPolicy(String pkg, Policy policy) {
2164 enforcePolicyAccess(pkg, "setNotificationPolicy");
2165 final long identity = Binder.clearCallingIdentity();
2167 mZenModeHelper.setNotificationPolicy(policy);
2169 Binder.restoreCallingIdentity(identity);
2174 public void applyAdjustmentFromRankerService(INotificationListener token,
2175 Adjustment adjustment) throws RemoteException {
2176 final long identity = Binder.clearCallingIdentity();
2178 synchronized (mNotificationList) {
2179 mRankerServices.checkServiceTokenLocked(token);
2180 applyAdjustmentLocked(adjustment);
2182 maybeAddAutobundleSummary(adjustment);
2183 mRankingHandler.requestSort();
2185 Binder.restoreCallingIdentity(identity);
2190 public void applyAdjustmentsFromRankerService(INotificationListener token,
2191 List<Adjustment> adjustments) throws RemoteException {
2193 final long identity = Binder.clearCallingIdentity();
2195 synchronized (mNotificationList) {
2196 mRankerServices.checkServiceTokenLocked(token);
2197 for (Adjustment adjustment : adjustments) {
2198 applyAdjustmentLocked(adjustment);
2201 for (Adjustment adjustment : adjustments) {
2202 maybeAddAutobundleSummary(adjustment);
2204 mRankingHandler.requestSort();
2206 Binder.restoreCallingIdentity(identity);
2211 private void applyAdjustmentLocked(Adjustment adjustment) {
2212 maybeClearAutobundleSummaryLocked(adjustment);
2213 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2217 if (adjustment.getImportance() != IMPORTANCE_NONE) {
2218 n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
2220 if (adjustment.getSignals() != null) {
2221 Bundle.setDefusable(adjustment.getSignals(), true);
2222 final String autoGroupKey = adjustment.getSignals().getString(
2223 Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2224 if (autoGroupKey == null) {
2225 EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
2227 EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
2229 n.sbn.setOverrideGroupKey(autoGroupKey);
2233 // Clears the 'fake' auto-bunding summary.
2234 private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
2235 if (adjustment.getSignals() != null) {
2236 Bundle.setDefusable(adjustment.getSignals(), true);
2237 if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
2238 && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2239 ArrayMap<String, String> summaries =
2240 mAutobundledSummaries.get(adjustment.getUser());
2241 if (summaries != null && summaries.containsKey(adjustment.getPackage())) {
2243 final NotificationRecord removed = mNotificationsByKey.get(
2244 summaries.remove(adjustment.getPackage()));
2245 if (removed != null) {
2246 mNotificationList.remove(removed);
2247 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2254 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2255 private void maybeAddAutobundleSummary(Adjustment adjustment) {
2256 if (adjustment.getSignals() != null) {
2257 Bundle.setDefusable(adjustment.getSignals(), true);
2258 if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2259 final String newAutoBundleKey =
2260 adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2262 NotificationRecord summaryRecord = null;
2263 synchronized (mNotificationList) {
2264 NotificationRecord notificationRecord =
2265 mNotificationsByKey.get(adjustment.getKey());
2266 if (notificationRecord == null) {
2267 // The notification could have been cancelled again already. A successive
2268 // adjustment will post a summary if needed.
2271 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2272 userId = adjustedSbn.getUser().getIdentifier();
2273 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2274 if (summaries == null) {
2275 summaries = new ArrayMap<>();
2277 mAutobundledSummaries.put(userId, summaries);
2278 if (!summaries.containsKey(adjustment.getPackage())
2279 && newAutoBundleKey != null) {
2281 final ApplicationInfo appInfo =
2282 adjustedSbn.getNotification().extras.getParcelable(
2283 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2284 final Bundle extras = new Bundle();
2285 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2286 final Notification summaryNotification =
2287 new Notification.Builder(getContext()).setSmallIcon(
2288 adjustedSbn.getNotification().getSmallIcon())
2289 .setGroupSummary(true)
2290 .setGroup(newAutoBundleKey)
2291 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2292 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2293 .setColor(adjustedSbn.getNotification().color)
2296 summaryNotification.extras.putAll(extras);
2297 Intent appIntent = getContext().getPackageManager()
2298 .getLaunchIntentForPackage(adjustment.getPackage());
2299 if (appIntent != null) {
2300 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2301 getContext(), 0, appIntent, 0, null,
2302 UserHandle.of(userId));
2304 final StatusBarNotification summarySbn =
2305 new StatusBarNotification(adjustedSbn.getPackageName(),
2306 adjustedSbn.getOpPkg(),
2307 Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2308 adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2309 summaryNotification, adjustedSbn.getUser(),
2311 System.currentTimeMillis());
2312 summaryRecord = new NotificationRecord(getContext(), summarySbn);
2313 summaries.put(adjustment.getPackage(), summarySbn.getKey());
2316 if (summaryRecord != null) {
2317 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2323 private String disableNotificationEffects(NotificationRecord record) {
2324 if (mDisableNotificationEffects) {
2325 return "booleanState";
2327 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2328 return "listenerHints";
2330 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2336 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2337 JSONObject dump = new JSONObject();
2339 dump.put("service", "Notification Manager");
2340 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2341 dump.put("ranking", mRankingHelper.dumpJson(filter));
2342 dump.put("stats", mUsageStats.dumpJson(filter));
2343 } catch (JSONException e) {
2344 e.printStackTrace();
2349 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2350 pw.print("Current Notification Manager state");
2351 if (filter.filtered) {
2352 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2356 final boolean zenOnly = filter.filtered && filter.zen;
2359 synchronized (mToastQueue) {
2360 N = mToastQueue.size();
2362 pw.println(" Toast Queue:");
2363 for (int i=0; i<N; i++) {
2364 mToastQueue.get(i).dump(pw, " ", filter);
2371 synchronized (mNotificationList) {
2373 N = mNotificationList.size();
2375 pw.println(" Notification List:");
2376 for (int i=0; i<N; i++) {
2377 final NotificationRecord nr = mNotificationList.get(i);
2378 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2379 nr.dump(pw, " ", getContext(), filter.redact);
2384 if (!filter.filtered) {
2387 pw.println(" Lights List:");
2388 for (int i=0; i<N; i++) {
2394 pw.println(mLights.get(i));
2398 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
2399 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
2400 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
2401 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
2402 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
2403 pw.println(" mCallState=" + callStateToString(mCallState));
2404 pw.println(" mSystemReady=" + mSystemReady);
2405 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
2407 pw.println(" mArchive=" + mArchive.toString());
2408 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2410 while (iter.hasNext()) {
2411 final StatusBarNotification sbn = iter.next();
2412 if (filter != null && !filter.matches(sbn)) continue;
2413 pw.println(" " + sbn);
2415 if (iter.hasNext()) pw.println(" ...");
2422 pw.println("\n Usage Stats:");
2423 mUsageStats.dump(pw, " ", filter);
2426 if (!filter.filtered || zenOnly) {
2427 pw.println("\n Zen Mode:");
2428 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
2429 mZenModeHelper.dump(pw, " ");
2431 pw.println("\n Zen Log:");
2432 ZenLog.dump(pw, " ");
2436 pw.println("\n Ranking Config:");
2437 mRankingHelper.dump(pw, " ", filter);
2439 pw.println("\n Notification listeners:");
2440 mListeners.dump(pw, filter);
2441 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2442 pw.print(" mListenersDisablingEffects: (");
2443 N = mListenersDisablingEffects.size();
2444 for (int i = 0; i < N; i++) {
2445 final int hint = mListenersDisablingEffects.keyAt(i);
2446 if (i > 0) pw.print(';');
2447 pw.print("hint[" + hint + "]:");
2449 final ArraySet<ManagedServiceInfo> listeners =
2450 mListenersDisablingEffects.valueAt(i);
2451 final int listenerSize = listeners.size();
2453 for (int j = 0; j < listenerSize; j++) {
2454 if (i > 0) pw.print(',');
2455 final ManagedServiceInfo listener = listeners.valueAt(i);
2456 pw.print(listener.component);
2460 pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
2461 pw.println("\n Notification ranker services:");
2462 mRankerServices.dump(pw, filter);
2464 pw.println("\n Policy access:");
2465 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
2467 pw.println("\n Condition providers:");
2468 mConditionProviders.dump(pw, filter);
2470 pw.println("\n Group summaries:");
2471 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2472 NotificationRecord r = entry.getValue();
2473 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2474 if (mNotificationsByKey.get(r.getKey()) != r) {
2475 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
2476 r.dump(pw, " ", getContext(), filter.redact);
2483 * The private API only accessible to the system process.
2485 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2487 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2488 String tag, int id, Notification notification, int[] idReceived, int userId) {
2489 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2490 idReceived, userId);
2494 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2496 checkCallerIsSystem();
2497 synchronized (mNotificationList) {
2498 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2500 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2501 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2504 NotificationRecord r = mNotificationList.get(i);
2505 StatusBarNotification sbn = r.sbn;
2506 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2507 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2508 // we have to revert to the flags we received initially *and* force remove
2509 // FLAG_FOREGROUND_SERVICE.
2510 sbn.getNotification().flags =
2511 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2512 mRankingHelper.sort(mNotificationList);
2513 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2518 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2519 final int callingPid, final String tag, final int id, final Notification notification,
2520 int[] idOut, int incomingUserId) {
2522 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2523 + " notification=" + notification);
2525 checkCallerIsSystemOrSameApp(pkg);
2526 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2527 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2529 final int userId = ActivityManager.handleIncomingUser(callingPid,
2530 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2531 final UserHandle user = new UserHandle(userId);
2533 // Fix the notification as best we can.
2535 final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2536 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2537 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2538 Notification.addFieldsFromContext(ai, userId, notification);
2539 } catch (NameNotFoundException e) {
2540 Slog.e(TAG, "Cannot create a context for sending app", e);
2544 mUsageStats.registerEnqueuedByApp(pkg);
2547 if (pkg == null || notification == null) {
2548 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2549 + " id=" + id + " notification=" + notification);
2551 final StatusBarNotification n = new StatusBarNotification(
2552 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2555 // Limit the number of notifications that any given package except the android
2556 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2557 if (!isSystemNotification && !isNotificationFromListener) {
2558 synchronized (mNotificationList) {
2559 if(mNotificationsByKey.get(n.getKey()) != null) {
2560 // this is an update, rate limit updates only
2561 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
2562 if (appEnqueueRate > mMaxPackageEnqueueRate) {
2563 mUsageStats.registerOverRateQuota(pkg);
2564 final long now = SystemClock.elapsedRealtime();
2565 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
2566 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
2567 + ". Shedding events. package=" + pkg);
2568 mLastOverRateLogTime = now;
2575 final int N = mNotificationList.size();
2576 for (int i=0; i<N; i++) {
2577 final NotificationRecord r = mNotificationList.get(i);
2578 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2579 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2580 break; // Allow updating existing notification
2583 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2584 mUsageStats.registerOverCountQuota(pkg);
2585 Slog.e(TAG, "Package has already posted " + count
2586 + " notifications. Not showing more. package=" + pkg);
2594 // Whitelist pending intents.
2595 if (notification.allPendingIntents != null) {
2596 final int intentCount = notification.allPendingIntents.size();
2597 if (intentCount > 0) {
2598 final ActivityManagerInternal am = LocalServices
2599 .getService(ActivityManagerInternal.class);
2600 final long duration = LocalServices.getService(
2601 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
2602 for (int i = 0; i < intentCount; i++) {
2603 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
2604 if (pendingIntent != null) {
2605 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
2612 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2613 Notification.PRIORITY_MAX);
2615 // setup local book-keeping
2616 final NotificationRecord r = new NotificationRecord(getContext(), n);
2617 mHandler.post(new EnqueueNotificationRunnable(userId, r));
2622 private class EnqueueNotificationRunnable implements Runnable {
2623 private final NotificationRecord r;
2624 private final int userId;
2626 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2627 this.userId = userId;
2634 synchronized (mNotificationList) {
2635 final StatusBarNotification n = r.sbn;
2636 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
2637 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2639 // Retain ranking information from previous record
2640 r.copyRankingInformation(old);
2643 final int callingUid = n.getUid();
2644 final int callingPid = n.getInitialPid();
2645 final Notification notification = n.getNotification();
2646 final String pkg = n.getPackageName();
2647 final int id = n.getId();
2648 final String tag = n.getTag();
2649 final boolean isSystemNotification = isUidSystem(callingUid) ||
2650 ("android".equals(pkg));
2652 // Handle grouped notifications and bail out early if we
2653 // can to avoid extracting signals.
2654 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2656 // This conditional is a dirty hack to limit the logging done on
2657 // behalf of the download manager without affecting other apps.
2658 if (!pkg.equals("com.android.providers.downloads")
2659 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2660 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2662 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2664 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2665 pkg, id, tag, userId, notification.toString(),
2669 mRankingHelper.extractSignals(r);
2671 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
2674 if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
2675 || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
2676 if (!isSystemNotification) {
2677 if (isPackageSuspended) {
2678 Slog.e(TAG, "Suppressing notification from package due to package "
2679 + "suspended by administrator.");
2680 mUsageStats.registerSuspendedByAdmin(r);
2682 Slog.e(TAG, "Suppressing notification from package by user request.");
2683 mUsageStats.registerBlocked(r);
2689 // tell the ranker service about the notification
2690 if (mRankerServices.isEnabled()) {
2691 mRankerServices.onNotificationEnqueued(r);
2692 // TODO delay the code below here for 100ms or until there is an answer
2696 int index = indexOfNotificationLocked(n.getKey());
2698 mNotificationList.add(r);
2699 mUsageStats.registerPostedByApp(r);
2701 old = mNotificationList.get(index);
2702 mNotificationList.set(index, r);
2703 mUsageStats.registerUpdatedByApp(r, old);
2704 // Make sure we don't lose the foreground service state.
2705 notification.flags |=
2706 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2710 mNotificationsByKey.put(n.getKey(), r);
2712 // Ensure if this is a foreground service that the proper additional
2714 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2715 notification.flags |= Notification.FLAG_ONGOING_EVENT
2716 | Notification.FLAG_NO_CLEAR;
2719 applyZenModeLocked(r);
2720 mRankingHelper.sort(mNotificationList);
2722 if (notification.getSmallIcon() != null) {
2723 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2724 mListeners.notifyPostedLocked(n, oldSbn);
2726 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2727 if (old != null && !old.isCanceled) {
2728 mListeners.notifyRemovedLocked(n);
2730 // ATTENTION: in a future release we will bail out here
2731 // so that we do not play sounds, show lights, etc. for invalid
2733 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2734 + n.getPackageName());
2737 buzzBeepBlinkLocked(r);
2743 * Ensures that grouped notification receive their special treatment.
2745 * <p>Cancels group children if the new notification causes a group to lose
2748 * <p>Updates mSummaryByGroupKey.</p>
2750 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2751 int callingUid, int callingPid) {
2752 StatusBarNotification sbn = r.sbn;
2753 Notification n = sbn.getNotification();
2754 if (n.isGroupSummary() && !sbn.isAppGroup()) {
2755 // notifications without a group shouldn't be a summary, otherwise autobundling can
2757 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
2760 String group = sbn.getGroupKey();
2761 boolean isSummary = n.isGroupSummary();
2763 Notification oldN = old != null ? old.sbn.getNotification() : null;
2764 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2765 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2768 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2769 if (removedSummary != old) {
2771 removedSummary != null ? removedSummary.getKey() : "<null>";
2772 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2773 ", removed=" + removedKey);
2777 mSummaryByGroupKey.put(group, r);
2780 // Clear out group children of the old notification if the update
2781 // causes the group summary to go away. This happens when the old
2782 // notification was a summary and the new one isn't, or when the old
2783 // notification was a summary and its group key changed.
2784 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2785 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2786 REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
2791 void buzzBeepBlinkLocked(NotificationRecord record) {
2792 boolean buzz = false;
2793 boolean beep = false;
2794 boolean blink = false;
2796 final Notification notification = record.sbn.getNotification();
2797 final String key = record.getKey();
2799 // Should this notification make noise, vibe, or use the LED?
2800 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
2801 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2802 if (DBG || record.isIntercepted())
2804 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2805 " intercept=" + record.isIntercepted()
2808 final int currentUser;
2809 final long token = Binder.clearCallingIdentity();
2811 currentUser = ActivityManager.getCurrentUser();
2813 Binder.restoreCallingIdentity(token);
2816 // If we're not supposed to beep, vibrate, etc. then don't.
2817 final String disableEffects = disableNotificationEffects(record);
2818 if (disableEffects != null) {
2819 ZenLog.traceDisableEffects(record, disableEffects);
2822 // Remember if this notification already owns the notification channels.
2823 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2824 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2826 // These are set inside the conditional if the notification is allowed to make noise.
2827 boolean hasValidVibrate = false;
2828 boolean hasValidSound = false;
2829 if (disableEffects == null
2830 && (record.getUserId() == UserHandle.USER_ALL ||
2831 record.getUserId() == currentUser ||
2832 mUserProfiles.isCurrentProfile(record.getUserId()))
2835 && mAudioManager != null) {
2836 if (DBG) Slog.v(TAG, "Interrupting!");
2838 // should we use the default notification sound? (indicated either by
2839 // DEFAULT_SOUND or because notification.sound is pointing at
2840 // Settings.System.NOTIFICATION_SOUND)
2841 final boolean useDefaultSound =
2842 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2843 Settings.System.DEFAULT_NOTIFICATION_URI
2844 .equals(notification.sound);
2846 Uri soundUri = null;
2847 if (useDefaultSound) {
2848 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2850 // check to see if the default notification sound is silent
2851 hasValidSound = mSystemNotificationSound != null;
2852 } else if (notification.sound != null) {
2853 soundUri = notification.sound;
2854 hasValidSound = (soundUri != null);
2857 // Does the notification want to specify its own vibration?
2858 final boolean hasCustomVibrate = notification.vibrate != null;
2860 // new in 4.2: if there was supposed to be a sound and we're in vibrate
2861 // mode, and no other vibration is specified, we fall back to vibration
2862 final boolean convertSoundToVibration =
2865 && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
2867 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
2868 final boolean useDefaultVibrate =
2869 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
2871 hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
2874 // We can alert, and we're allowed to alert, but if the developer asked us to only do
2875 // it once, and we already have, then don't.
2876 if (!(record.isUpdate
2877 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
2879 sendAccessibilityEvent(notification, record.sbn.getPackageName());
2881 if (hasValidSound) {
2883 (notification.flags & Notification.FLAG_INSISTENT) != 0;
2884 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
2885 mSoundNotificationKey = key;
2886 // do not play notifications if stream volume is 0 (typically because
2887 // ringer mode is silent) or if there is a user of exclusive audio focus
2888 if ((mAudioManager.getStreamVolume(
2889 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
2890 && !mAudioManager.isAudioFocusExclusive()) {
2891 final long identity = Binder.clearCallingIdentity();
2893 final IRingtonePlayer player =
2894 mAudioManager.getRingtonePlayer();
2895 if (player != null) {
2896 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
2897 + " with attributes " + audioAttributes);
2898 player.playAsync(soundUri, record.sbn.getUser(), looping,
2902 } catch (RemoteException e) {
2904 Binder.restoreCallingIdentity(identity);
2909 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
2910 == AudioManager.RINGER_MODE_SILENT)) {
2911 mVibrateNotificationKey = key;
2913 if (useDefaultVibrate || convertSoundToVibration) {
2914 // Escalate privileges so we can use the vibrator even if the
2915 // notifying app does not have the VIBRATE permission.
2916 long identity = Binder.clearCallingIdentity();
2918 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2919 useDefaultVibrate ? mDefaultVibrationPattern
2920 : mFallbackVibrationPattern,
2921 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2922 ? 0: -1, audioAttributesForNotification(notification));
2925 Binder.restoreCallingIdentity(identity);
2927 } else if (notification.vibrate.length > 1) {
2928 // If you want your own vibration pattern, you need the VIBRATE
2930 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
2931 notification.vibrate,
2932 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
2933 ? 0: -1, audioAttributesForNotification(notification));
2940 // If a notification is updated to remove the actively playing sound or vibrate,
2941 // cancel that feedback now
2942 if (wasBeep && !hasValidSound) {
2945 if (wasBuzz && !hasValidVibrate) {
2946 clearVibrateLocked();
2950 // release the light
2951 boolean wasShowLights = mLights.remove(key);
2952 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
2953 && ((record.getSuppressedVisualEffects()
2954 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
2956 updateLightsLocked();
2957 if (mUseAttentionLight) {
2958 mAttentionLight.pulse();
2961 } else if (wasShowLights) {
2962 updateLightsLocked();
2964 if (buzz || beep || blink) {
2965 if (((record.getSuppressedVisualEffects()
2966 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
2967 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
2969 EventLogTags.writeNotificationAlert(key,
2970 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
2971 mHandler.post(mBuzzBeepBlinked);
2976 private static AudioAttributes audioAttributesForNotification(Notification n) {
2977 if (n.audioAttributes != null
2978 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
2979 // the audio attributes are set and different from the default, use them
2980 return n.audioAttributes;
2981 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
2982 // the stream type is valid, use it
2983 return new AudioAttributes.Builder()
2984 .setInternalLegacyStreamType(n.audioStreamType)
2986 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
2987 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2989 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
2990 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
2994 void showNextToastLocked() {
2995 ToastRecord record = mToastQueue.get(0);
2996 while (record != null) {
2997 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
2999 record.callback.show(record.token);
3000 scheduleTimeoutLocked(record);
3002 } catch (RemoteException e) {
3003 Slog.w(TAG, "Object died trying to show notification " + record.callback
3004 + " in package " + record.pkg);
3005 // remove it from the list and let the process die
3006 int index = mToastQueue.indexOf(record);
3008 mToastQueue.remove(index);
3010 keepProcessAliveIfNeededLocked(record.pid);
3011 if (mToastQueue.size() > 0) {
3012 record = mToastQueue.get(0);
3020 void cancelToastLocked(int index) {
3021 ToastRecord record = mToastQueue.get(index);
3023 record.callback.hide();
3024 } catch (RemoteException e) {
3025 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3026 + " in package " + record.pkg);
3027 // don't worry about this, we're about to remove it from
3031 ToastRecord lastToast = mToastQueue.remove(index);
3032 mWindowManagerInternal.removeWindowToken(lastToast.token, true);
3034 keepProcessAliveIfNeededLocked(record.pid);
3035 if (mToastQueue.size() > 0) {
3036 // Show the next one. If the callback fails, this will remove
3037 // it from the list, so don't assume that the list hasn't changed
3038 // after this point.
3039 showNextToastLocked();
3043 private void scheduleTimeoutLocked(ToastRecord r)
3045 mHandler.removeCallbacksAndMessages(r);
3046 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3047 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3048 mHandler.sendMessageDelayed(m, delay);
3051 private void handleTimeout(ToastRecord record)
3053 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3054 synchronized (mToastQueue) {
3055 int index = indexOfToastLocked(record.pkg, record.callback);
3057 cancelToastLocked(index);
3062 // lock on mToastQueue
3063 int indexOfToastLocked(String pkg, ITransientNotification callback)
3065 IBinder cbak = callback.asBinder();
3066 ArrayList<ToastRecord> list = mToastQueue;
3067 int len = list.size();
3068 for (int i=0; i<len; i++) {
3069 ToastRecord r = list.get(i);
3070 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3077 // lock on mToastQueue
3078 void keepProcessAliveIfNeededLocked(int pid)
3080 int toastCount = 0; // toasts from this pid
3081 ArrayList<ToastRecord> list = mToastQueue;
3082 int N = list.size();
3083 for (int i=0; i<N; i++) {
3084 ToastRecord r = list.get(i);
3090 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
3091 } catch (RemoteException e) {
3092 // Shouldn't happen.
3096 private void handleRankingReconsideration(Message message) {
3097 if (!(message.obj instanceof RankingReconsideration)) return;
3098 RankingReconsideration recon = (RankingReconsideration) message.obj;
3101 synchronized (mNotificationList) {
3102 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3103 if (record == null) {
3106 int indexBefore = findNotificationRecordIndexLocked(record);
3107 boolean interceptBefore = record.isIntercepted();
3108 int visibilityBefore = record.getPackageVisibilityOverride();
3109 recon.applyChangesLocked(record);
3110 applyZenModeLocked(record);
3111 mRankingHelper.sort(mNotificationList);
3112 int indexAfter = findNotificationRecordIndexLocked(record);
3113 boolean interceptAfter = record.isIntercepted();
3114 int visibilityAfter = record.getPackageVisibilityOverride();
3115 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3116 || visibilityBefore != visibilityAfter;
3117 if (interceptBefore && !interceptAfter) {
3118 buzzBeepBlinkLocked(record);
3122 scheduleSendRankingUpdate();
3126 private void handleRankingSort() {
3127 synchronized (mNotificationList) {
3128 final int N = mNotificationList.size();
3129 ArrayList<String> orderBefore = new ArrayList<String>(N);
3130 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
3131 int[] visibilities = new int[N];
3132 int[] importances = new int[N];
3133 for (int i = 0; i < N; i++) {
3134 final NotificationRecord r = mNotificationList.get(i);
3135 orderBefore.add(r.getKey());
3136 groupOverrideBefore.add(r.sbn.getGroupKey());
3137 visibilities[i] = r.getPackageVisibilityOverride();
3138 importances[i] = r.getImportance();
3139 mRankingHelper.extractSignals(r);
3141 mRankingHelper.sort(mNotificationList);
3142 for (int i = 0; i < N; i++) {
3143 final NotificationRecord r = mNotificationList.get(i);
3144 if (!orderBefore.get(i).equals(r.getKey())
3145 || visibilities[i] != r.getPackageVisibilityOverride()
3146 || importances[i] != r.getImportance()
3147 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
3148 scheduleSendRankingUpdate();
3155 private void recordCallerLocked(NotificationRecord record) {
3156 if (mZenModeHelper.isCall(record)) {
3157 mZenModeHelper.recordCaller(record);
3161 // let zen mode evaluate this record
3162 private void applyZenModeLocked(NotificationRecord record) {
3163 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
3164 if (record.isIntercepted()) {
3165 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3166 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3167 | (mZenModeHelper.shouldSuppressWhenScreenOn()
3168 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
3169 record.setSuppressedVisualEffects(suppressed);
3173 // lock on mNotificationList
3174 private int findNotificationRecordIndexLocked(NotificationRecord target) {
3175 return mRankingHelper.indexOf(mNotificationList, target);
3178 private void scheduleSendRankingUpdate() {
3179 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3180 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3181 mHandler.sendMessage(m);
3185 private void handleSendRankingUpdate() {
3186 synchronized (mNotificationList) {
3187 mListeners.notifyRankingUpdateLocked();
3191 private void scheduleListenerHintsChanged(int state) {
3192 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3193 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
3196 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3197 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3198 mHandler.obtainMessage(
3199 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3200 listenerInterruptionFilter,
3204 private void handleListenerHintsChanged(int hints) {
3205 synchronized (mNotificationList) {
3206 mListeners.notifyListenerHintsChangedLocked(hints);
3210 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3211 synchronized (mNotificationList) {
3212 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3216 private final class WorkerHandler extends Handler
3219 public void handleMessage(Message msg)
3223 case MESSAGE_TIMEOUT:
3224 handleTimeout((ToastRecord)msg.obj);
3226 case MESSAGE_SAVE_POLICY_FILE:
3227 handleSavePolicyFile();
3229 case MESSAGE_SEND_RANKING_UPDATE:
3230 handleSendRankingUpdate();
3232 case MESSAGE_LISTENER_HINTS_CHANGED:
3233 handleListenerHintsChanged(msg.arg1);
3235 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3236 handleListenerInterruptionFilterChanged(msg.arg1);
3243 private final class RankingHandlerWorker extends Handler implements RankingHandler
3245 public RankingHandlerWorker(Looper looper) {
3250 public void handleMessage(Message msg) {
3252 case MESSAGE_RECONSIDER_RANKING:
3253 handleRankingReconsideration(msg);
3255 case MESSAGE_RANKING_SORT:
3256 handleRankingSort();
3261 public void requestSort() {
3262 removeMessages(MESSAGE_RANKING_SORT);
3263 sendEmptyMessage(MESSAGE_RANKING_SORT);
3266 public void requestReconsideration(RankingReconsideration recon) {
3267 Message m = Message.obtain(this,
3268 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3269 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3270 sendMessageDelayed(m, delay);
3275 // ============================================================================
3276 static int clamp(int x, int low, int high) {
3277 return (x < low) ? low : ((x > high) ? high : x);
3280 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3281 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
3282 if (!manager.isEnabled()) {
3286 AccessibilityEvent event =
3287 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3288 event.setPackageName(packageName);
3289 event.setClassName(Notification.class.getName());
3290 event.setParcelableData(notification);
3291 CharSequence tickerText = notification.tickerText;
3292 if (!TextUtils.isEmpty(tickerText)) {
3293 event.getText().add(tickerText);
3296 manager.sendAccessibilityEvent(event);
3299 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
3302 recordCallerLocked(r);
3306 if (r.getNotification().deleteIntent != null) {
3308 r.getNotification().deleteIntent.send();
3309 } catch (PendingIntent.CanceledException ex) {
3310 // do nothing - there's no relevant way to recover, and
3311 // no reason to let this propagate
3312 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
3318 if (r.getNotification().getSmallIcon() != null) {
3319 r.isCanceled = true;
3320 mListeners.notifyRemovedLocked(r.sbn);
3323 final String canceledKey = r.getKey();
3326 if (canceledKey.equals(mSoundNotificationKey)) {
3327 mSoundNotificationKey = null;
3328 final long identity = Binder.clearCallingIdentity();
3330 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3331 if (player != null) {
3334 } catch (RemoteException e) {
3336 Binder.restoreCallingIdentity(identity);
3341 if (canceledKey.equals(mVibrateNotificationKey)) {
3342 mVibrateNotificationKey = null;
3343 long identity = Binder.clearCallingIdentity();
3348 Binder.restoreCallingIdentity(identity);
3353 mLights.remove(canceledKey);
3355 // Record usage stats
3356 // TODO: add unbundling stats?
3358 case REASON_DELEGATE_CANCEL:
3359 case REASON_DELEGATE_CANCEL_ALL:
3360 case REASON_LISTENER_CANCEL:
3361 case REASON_LISTENER_CANCEL_ALL:
3362 mUsageStats.registerDismissedByUser(r);
3364 case REASON_APP_CANCEL:
3365 case REASON_APP_CANCEL_ALL:
3366 mUsageStats.registerRemovedByApp(r);
3370 mNotificationsByKey.remove(r.sbn.getKey());
3371 String groupKey = r.getGroupKey();
3372 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3373 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3374 mSummaryByGroupKey.remove(groupKey);
3376 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
3377 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
3378 summaries.remove(r.sbn.getPackageName());
3381 // Save it for users of getHistoricalNotifications()
3382 mArchive.record(r.sbn);
3384 final long now = System.currentTimeMillis();
3385 EventLogTags.writeNotificationCanceled(canceledKey, reason,
3386 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
3390 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
3391 * and none of the {@code mustNotHaveFlags}.
3393 void cancelNotification(final int callingUid, final int callingPid,
3394 final String pkg, final String tag, final int id,
3395 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
3396 final int userId, final int reason, final ManagedServiceInfo listener) {
3397 // In enqueueNotificationInternal notifications are added by scheduling the
3398 // work on the worker handler. Hence, we also schedule the cancel on this
3399 // handler to avoid a scenario where an add notification call followed by a
3400 // remove notification call ends up in not removing the notification.
3401 mHandler.post(new Runnable() {
3404 String listenerName = listener == null ? null : listener.component.toShortString();
3405 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3406 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
3408 synchronized (mNotificationList) {
3409 int index = indexOfNotificationLocked(pkg, tag, id, userId);
3411 NotificationRecord r = mNotificationList.get(index);
3413 // Ideally we'd do this in the caller of this method. However, that would
3414 // require the caller to also find the notification.
3415 if (reason == REASON_DELEGATE_CLICK) {
3416 mUsageStats.registerClickedByUser(r);
3419 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3422 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3426 mNotificationList.remove(index);
3428 cancelNotificationLocked(r, sendDelete, reason);
3429 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3430 REASON_GROUP_SUMMARY_CANCELED, sendDelete);
3431 updateLightsLocked();
3439 * Determine whether the userId applies to the notification in question, either because
3440 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3442 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3444 // looking for USER_ALL notifications? match everything
3445 userId == UserHandle.USER_ALL
3446 // a notification sent to USER_ALL matches any query
3447 || r.getUserId() == UserHandle.USER_ALL
3448 // an exact user match
3449 || r.getUserId() == userId;
3453 * Determine whether the userId applies to the notification in question, either because
3454 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
3455 * because it matches one of the users profiles.
3457 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
3458 return notificationMatchesUserId(r, userId)
3459 || mUserProfiles.isCurrentProfile(r.getUserId());
3463 * Cancels all notifications from a given package that have all of the
3464 * {@code mustHaveFlags}.
3466 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
3467 int mustNotHaveFlags, boolean doit, int userId, int reason,
3468 ManagedServiceInfo listener) {
3469 String listenerName = listener == null ? null : listener.component.toShortString();
3470 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3471 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
3474 synchronized (mNotificationList) {
3475 final int N = mNotificationList.size();
3476 ArrayList<NotificationRecord> canceledNotifications = null;
3477 for (int i = N-1; i >= 0; --i) {
3478 NotificationRecord r = mNotificationList.get(i);
3479 if (!notificationMatchesUserId(r, userId)) {
3482 // Don't remove notifications to all, if there's no package name specified
3483 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
3486 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
3489 if ((r.getFlags() & mustNotHaveFlags) != 0) {
3492 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
3495 if (canceledNotifications == null) {
3496 canceledNotifications = new ArrayList<>();
3498 canceledNotifications.add(r);
3502 mNotificationList.remove(i);
3503 cancelNotificationLocked(r, false, reason);
3505 if (doit && canceledNotifications != null) {
3506 final int M = canceledNotifications.size();
3507 for (int i = 0; i < M; i++) {
3508 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3509 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
3512 if (canceledNotifications != null) {
3513 updateLightsLocked();
3515 return canceledNotifications != null;
3519 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
3520 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
3521 String listenerName = listener == null ? null : listener.component.toShortString();
3522 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3523 null, userId, 0, 0, reason, listenerName);
3525 ArrayList<NotificationRecord> canceledNotifications = null;
3526 final int N = mNotificationList.size();
3527 for (int i=N-1; i>=0; i--) {
3528 NotificationRecord r = mNotificationList.get(i);
3529 if (includeCurrentProfiles) {
3530 if (!notificationMatchesCurrentProfiles(r, userId)) {
3534 if (!notificationMatchesUserId(r, userId)) {
3539 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3540 | Notification.FLAG_NO_CLEAR)) == 0) {
3541 mNotificationList.remove(i);
3542 cancelNotificationLocked(r, true, reason);
3543 // Make a note so we can cancel children later.
3544 if (canceledNotifications == null) {
3545 canceledNotifications = new ArrayList<>();
3547 canceledNotifications.add(r);
3550 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3551 for (int i = 0; i < M; i++) {
3552 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3553 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
3555 updateLightsLocked();
3558 // Warning: The caller is responsible for invoking updateLightsLocked().
3559 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3560 String listenerName, int reason, boolean sendDelete) {
3561 Notification n = r.getNotification();
3562 if (!n.isGroupSummary()) {
3566 String pkg = r.sbn.getPackageName();
3567 int userId = r.getUserId();
3570 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3574 final int N = mNotificationList.size();
3575 for (int i = N - 1; i >= 0; i--) {
3576 NotificationRecord childR = mNotificationList.get(i);
3577 StatusBarNotification childSbn = childR.sbn;
3578 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
3579 childR.getGroupKey().equals(r.getGroupKey())) {
3580 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3581 childSbn.getTag(), userId, 0, 0, reason, listenerName);
3582 mNotificationList.remove(i);
3583 cancelNotificationLocked(childR, sendDelete, reason);
3588 // lock on mNotificationList
3589 void updateLightsLocked()
3591 // handle notification lights
3592 NotificationRecord ledNotification = null;
3593 while (ledNotification == null && !mLights.isEmpty()) {
3594 final String owner = mLights.get(mLights.size() - 1);
3595 ledNotification = mNotificationsByKey.get(owner);
3596 if (ledNotification == null) {
3597 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3598 mLights.remove(owner);
3602 // Don't flash while we are in a call or screen is on
3603 if (ledNotification == null || mInCall || mScreenOn) {
3604 mNotificationLight.turnOff();
3605 if (mStatusBar != null) {
3606 mStatusBar.notificationLightOff();
3609 final Notification ledno = ledNotification.sbn.getNotification();
3610 int ledARGB = ledno.ledARGB;
3611 int ledOnMS = ledno.ledOnMS;
3612 int ledOffMS = ledno.ledOffMS;
3613 if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3614 ledARGB = mDefaultNotificationColor;
3615 ledOnMS = mDefaultNotificationLedOn;
3616 ledOffMS = mDefaultNotificationLedOff;
3618 if (mNotificationPulseEnabled) {
3620 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3623 if (mStatusBar != null) {
3624 // let SystemUI make an independent decision
3625 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3630 // lock on mNotificationList
3631 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3633 ArrayList<NotificationRecord> list = mNotificationList;
3634 final int len = list.size();
3635 for (int i=0; i<len; i++) {
3636 NotificationRecord r = list.get(i);
3637 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3638 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3645 // lock on mNotificationList
3646 int indexOfNotificationLocked(String key) {
3647 final int N = mNotificationList.size();
3648 for (int i = 0; i < N; i++) {
3649 if (key.equals(mNotificationList.get(i).getKey())) {
3656 private void updateNotificationPulse() {
3657 synchronized (mNotificationList) {
3658 updateLightsLocked();
3662 private static boolean isUidSystem(int uid) {
3663 final int appid = UserHandle.getAppId(uid);
3664 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3667 private static boolean isCallerSystem() {
3668 return isUidSystem(Binder.getCallingUid());
3671 private static void checkCallerIsSystem() {
3672 if (isCallerSystem()) {
3675 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3678 private static void checkCallerIsSystemOrSameApp(String pkg) {
3679 if (isCallerSystem()) {
3682 checkCallerIsSameApp(pkg);
3685 private static void checkCallerIsSameApp(String pkg) {
3686 final int uid = Binder.getCallingUid();
3688 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3689 pkg, 0, UserHandle.getCallingUserId());
3691 throw new SecurityException("Unknown package " + pkg);
3693 if (!UserHandle.isSameApp(ai.uid, uid)) {
3694 throw new SecurityException("Calling uid " + uid + " gave package"
3695 + pkg + " which is owned by uid " + ai.uid);
3697 } catch (RemoteException re) {
3698 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3702 private static String callStateToString(int state) {
3704 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3705 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3706 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3707 default: return "CALL_STATE_UNKNOWN_" + state;
3711 private void listenForCallState() {
3712 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3714 public void onCallStateChanged(int state, String incomingNumber) {
3715 if (mCallState == state) return;
3716 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3719 }, PhoneStateListener.LISTEN_CALL_STATE);
3723 * Generates a NotificationRankingUpdate from 'sbns', considering only
3724 * notifications visible to the given listener.
3726 * <p>Caller must hold a lock on mNotificationList.</p>
3728 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3729 final int N = mNotificationList.size();
3730 ArrayList<String> keys = new ArrayList<String>(N);
3731 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3732 ArrayList<Integer> importance = new ArrayList<>(N);
3733 Bundle overrideGroupKeys = new Bundle();
3734 Bundle visibilityOverrides = new Bundle();
3735 Bundle suppressedVisualEffects = new Bundle();
3736 Bundle explanation = new Bundle();
3737 for (int i = 0; i < N; i++) {
3738 NotificationRecord record = mNotificationList.get(i);
3739 if (!isVisibleToListener(record.sbn, info)) {
3742 final String key = record.sbn.getKey();
3744 importance.add(record.getImportance());
3745 if (record.getImportanceExplanation() != null) {
3746 explanation.putCharSequence(key, record.getImportanceExplanation());
3748 if (record.isIntercepted()) {
3749 interceptedKeys.add(key);
3752 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
3753 if (record.getPackageVisibilityOverride()
3754 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3755 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
3757 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
3759 final int M = keys.size();
3760 String[] keysAr = keys.toArray(new String[M]);
3761 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
3762 int[] importanceAr = new int[M];
3763 for (int i = 0; i < M; i++) {
3764 importanceAr[i] = importance.get(i);
3766 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
3767 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
3770 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
3771 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
3774 // TODO: remove this for older listeners.
3778 private boolean isPackageSuspendedForUser(String pkg, int uid) {
3779 int userId = UserHandle.getUserId(uid);
3781 return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
3782 } catch (RemoteException re) {
3783 throw new SecurityException("Could not talk to package manager service");
3784 } catch (IllegalArgumentException ex) {
3785 // Package not found.
3790 private class TrimCache {
3791 StatusBarNotification heavy;
3792 StatusBarNotification sbnClone;
3793 StatusBarNotification sbnCloneLight;
3795 TrimCache(StatusBarNotification sbn) {
3799 StatusBarNotification ForListener(ManagedServiceInfo info) {
3800 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
3801 if (sbnCloneLight == null) {
3802 sbnCloneLight = heavy.cloneLight();
3804 return sbnCloneLight;
3806 if (sbnClone == null) {
3807 sbnClone = heavy.clone();
3814 public class NotificationRankers extends ManagedServices {
3816 public NotificationRankers() {
3817 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3821 protected Config getConfig() {
3822 Config c = new Config();
3823 c.caption = "notification ranker service";
3824 c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
3825 c.secureSettingName = null;
3826 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
3827 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
3828 c.clientLabel = R.string.notification_ranker_binding_label;
3833 protected IInterface asInterface(IBinder binder) {
3834 return INotificationListener.Stub.asInterface(binder);
3838 protected boolean checkType(IInterface service) {
3839 return service instanceof INotificationListener;
3843 protected void onServiceAdded(ManagedServiceInfo info) {
3844 mListeners.registerGuestService(info);
3848 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3849 mListeners.unregisterService(removed.service, removed.userid);
3852 public void onNotificationEnqueued(final NotificationRecord r) {
3853 final StatusBarNotification sbn = r.sbn;
3854 TrimCache trimCache = new TrimCache(sbn);
3856 // mServices is the list inside ManagedServices of all the rankers,
3857 // There should be only one, but it's a list, so while we enforce
3858 // singularity elsewhere, we keep it general here, to avoid surprises.
3859 for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
3860 boolean sbnVisible = isVisibleToListener(sbn, info);
3865 final int importance = r.getImportance();
3866 final boolean fromUser = r.isImportanceFromUser();
3867 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
3868 mHandler.post(new Runnable() {
3871 notifyEnqueued(info, sbnToPost, importance, fromUser);
3877 private void notifyEnqueued(final ManagedServiceInfo info,
3878 final StatusBarNotification sbn, int importance, boolean fromUser) {
3879 final INotificationListener ranker = (INotificationListener) info.service;
3880 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
3882 ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
3883 } catch (RemoteException ex) {
3884 Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
3888 public boolean isEnabled() {
3889 return !mServices.isEmpty();
3893 public void onUserSwitched(int user) {
3894 synchronized (mNotificationList) {
3895 int i = mServices.size()-1;
3897 final ManagedServiceInfo info = mServices.get(i);
3898 unregisterService(info.service, info.userid);
3905 public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
3906 if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
3907 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
3908 if (mRankerServicePackageName == null) {
3912 if (pkgList != null && (pkgList.length > 0) && !removingPackage) {
3913 for (String pkgName : pkgList) {
3914 if (mRankerServicePackageName.equals(pkgName)) {
3921 protected void registerRanker() {
3922 // Find the updatable ranker and register it.
3923 if (mRankerServicePackageName == null) {
3924 Slog.w(TAG, "could not start ranker service: no package specified!");
3927 Set<ComponentName> rankerComponents = queryPackageForServices(
3928 mRankerServicePackageName, UserHandle.USER_SYSTEM);
3929 Iterator<ComponentName> iterator = rankerComponents.iterator();
3930 if (iterator.hasNext()) {
3931 ComponentName rankerComponent = iterator.next();
3932 if (iterator.hasNext()) {
3933 Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
3935 registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
3938 Slog.w(TAG, "could not start ranker service: none found");
3943 public class NotificationListeners extends ManagedServices {
3945 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
3947 public NotificationListeners() {
3948 super(getContext(), mHandler, mNotificationList, mUserProfiles);
3952 protected Config getConfig() {
3953 Config c = new Config();
3954 c.caption = "notification listener";
3955 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
3956 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
3957 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
3958 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
3959 c.clientLabel = R.string.notification_listener_binding_label;
3964 protected IInterface asInterface(IBinder binder) {
3965 return INotificationListener.Stub.asInterface(binder);
3969 protected boolean checkType(IInterface service) {
3970 return service instanceof INotificationListener;
3974 public void onServiceAdded(ManagedServiceInfo info) {
3975 final INotificationListener listener = (INotificationListener) info.service;
3976 final NotificationRankingUpdate update;
3977 synchronized (mNotificationList) {
3978 update = makeRankingUpdateLocked(info);
3981 listener.onListenerConnected(update);
3982 } catch (RemoteException e) {
3988 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
3989 if (removeDisabledHints(removed)) {
3990 updateListenerHintsLocked();
3991 updateEffectsSuppressorLocked();
3993 mLightTrimListeners.remove(removed);
3996 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
3997 if (trim == TRIM_LIGHT) {
3998 mLightTrimListeners.add(info);
4000 mLightTrimListeners.remove(info);
4004 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
4005 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
4009 * asynchronously notify all listeners about a new notification
4012 * Also takes care of removing a notification that has been visible to a listener before,
4013 * but isn't anymore.
4015 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
4016 // Lazily initialized snapshots of the notification.
4017 TrimCache trimCache = new TrimCache(sbn);
4019 for (final ManagedServiceInfo info : mServices) {
4020 boolean sbnVisible = isVisibleToListener(sbn, info);
4021 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
4022 // This notification hasn't been and still isn't visible -> ignore.
4023 if (!oldSbnVisible && !sbnVisible) {
4026 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
4028 // This notification became invisible -> remove the old one.
4029 if (oldSbnVisible && !sbnVisible) {
4030 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
4031 mHandler.post(new Runnable() {
4034 notifyRemoved(info, oldSbnLightClone, update);
4040 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
4041 mHandler.post(new Runnable() {
4044 notifyPosted(info, sbnToPost, update);
4051 * asynchronously notify all listeners about a removed notification
4053 public void notifyRemovedLocked(StatusBarNotification sbn) {
4054 // make a copy in case changes are made to the underlying Notification object
4055 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
4057 final StatusBarNotification sbnLight = sbn.cloneLight();
4058 for (final ManagedServiceInfo info : mServices) {
4059 if (!isVisibleToListener(sbn, info)) {
4062 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
4063 mHandler.post(new Runnable() {
4066 notifyRemoved(info, sbnLight, update);
4073 * asynchronously notify all listeners about a reordering of notifications
4075 public void notifyRankingUpdateLocked() {
4076 for (final ManagedServiceInfo serviceInfo : mServices) {
4077 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4080 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
4081 mHandler.post(new Runnable() {
4084 notifyRankingUpdate(serviceInfo, update);
4090 public void notifyListenerHintsChangedLocked(final int hints) {
4091 for (final ManagedServiceInfo serviceInfo : mServices) {
4092 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4095 mHandler.post(new Runnable() {
4098 notifyListenerHintsChanged(serviceInfo, hints);
4104 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
4105 for (final ManagedServiceInfo serviceInfo : mServices) {
4106 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4109 mHandler.post(new Runnable() {
4112 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
4118 private void notifyPosted(final ManagedServiceInfo info,
4119 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
4120 final INotificationListener listener = (INotificationListener)info.service;
4121 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4123 listener.onNotificationPosted(sbnHolder, rankingUpdate);
4124 } catch (RemoteException ex) {
4125 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4129 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
4130 NotificationRankingUpdate rankingUpdate) {
4131 if (!info.enabledAndUserMatches(sbn.getUserId())) {
4134 final INotificationListener listener = (INotificationListener) info.service;
4135 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4137 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
4138 } catch (RemoteException ex) {
4139 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
4143 private void notifyRankingUpdate(ManagedServiceInfo info,
4144 NotificationRankingUpdate rankingUpdate) {
4145 final INotificationListener listener = (INotificationListener) info.service;
4147 listener.onNotificationRankingUpdate(rankingUpdate);
4148 } catch (RemoteException ex) {
4149 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4153 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
4154 final INotificationListener listener = (INotificationListener) info.service;
4156 listener.onListenerHintsChanged(hints);
4157 } catch (RemoteException ex) {
4158 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
4162 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4163 int interruptionFilter) {
4164 final INotificationListener listener = (INotificationListener) info.service;
4166 listener.onInterruptionFilterChanged(interruptionFilter);
4167 } catch (RemoteException ex) {
4168 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4172 private boolean isListenerPackage(String packageName) {
4173 if (packageName == null) {
4176 // TODO: clean up locking object later
4177 synchronized (mNotificationList) {
4178 for (final ManagedServiceInfo serviceInfo : mServices) {
4179 if (packageName.equals(serviceInfo.component.getPackageName())) {
4188 public static final class DumpFilter {
4189 public boolean filtered = false;
4190 public String pkgFilter;
4193 public boolean stats;
4194 public boolean redact = true;
4196 public static DumpFilter parseFromArguments(String[] args) {
4197 final DumpFilter filter = new DumpFilter();
4198 for (int ai = 0; ai < args.length; ai++) {
4199 final String a = args[ai];
4200 if ("--noredact".equals(a) || "--reveal".equals(a)) {
4201 filter.redact = false;
4202 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4203 if (ai < args.length-1) {
4205 filter.pkgFilter = args[ai].trim().toLowerCase();
4206 if (filter.pkgFilter.isEmpty()) {
4207 filter.pkgFilter = null;
4209 filter.filtered = true;
4212 } else if ("--zen".equals(a) || "zen".equals(a)) {
4213 filter.filtered = true;
4215 } else if ("--stats".equals(a)) {
4216 filter.stats = true;
4217 if (ai < args.length-1) {
4219 filter.since = Long.valueOf(args[ai]);
4228 public boolean matches(StatusBarNotification sbn) {
4229 if (!filtered) return true;
4230 return zen ? true : sbn != null
4231 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
4234 public boolean matches(ComponentName component) {
4235 if (!filtered) return true;
4236 return zen ? true : component != null && matches(component.getPackageName());
4239 public boolean matches(String pkg) {
4240 if (!filtered) return true;
4241 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
4245 public String toString() {
4246 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
4251 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4252 * binder without sending large amounts of data over a oneway transaction.
4254 private static final class StatusBarNotificationHolder
4255 extends IStatusBarNotificationHolder.Stub {
4256 private StatusBarNotification mValue;
4258 public StatusBarNotificationHolder(StatusBarNotification value) {
4262 /** Get the held value and clear it. This function should only be called once per holder */
4264 public StatusBarNotification get() {
4265 StatusBarNotification value = mValue;
4271 private final class PolicyAccess {
4272 private static final String SEPARATOR = ":";
4273 private final String[] PERM = {
4274 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4277 public boolean isPackageGranted(String pkg) {
4278 return pkg != null && getGrantedPackages().contains(pkg);
4281 public void put(String pkg, boolean granted) {
4282 if (pkg == null) return;
4283 final ArraySet<String> pkgs = getGrantedPackages();
4286 changed = pkgs.add(pkg);
4288 changed = pkgs.remove(pkg);
4290 if (!changed) return;
4291 final String setting = TextUtils.join(SEPARATOR, pkgs);
4292 final int currentUser = ActivityManager.getCurrentUser();
4293 Settings.Secure.putStringForUser(getContext().getContentResolver(),
4294 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4297 getContext().sendBroadcastAsUser(new Intent(NotificationManager
4298 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4300 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4303 public ArraySet<String> getGrantedPackages() {
4304 final ArraySet<String> pkgs = new ArraySet<>();
4306 long identity = Binder.clearCallingIdentity();
4308 final String setting = Settings.Secure.getStringForUser(
4309 getContext().getContentResolver(),
4310 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4311 ActivityManager.getCurrentUser());
4312 if (setting != null) {
4313 final String[] tokens = setting.split(SEPARATOR);
4314 for (int i = 0; i < tokens.length; i++) {
4315 String token = tokens[i];
4316 if (token != null) {
4317 token = token.trim();
4319 if (TextUtils.isEmpty(token)) {
4326 Binder.restoreCallingIdentity(identity);
4331 public String[] getRequestingPackages() throws RemoteException {
4332 final ParceledListSlice list = AppGlobals.getPackageManager()
4333 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4334 ActivityManager.getCurrentUser());
4335 final List<PackageInfo> pkgs = list.getList();
4336 if (pkgs == null || pkgs.isEmpty()) return new String[0];
4337 final int N = pkgs.size();
4338 final String[] rt = new String[N];
4339 for (int i = 0; i < N; i++) {
4340 rt[i] = pkgs.get(i).packageName;