2 * Copyright (C) 2007 The Android Open Source Project
3 * Copyright (C) 2015 The CyanogenMod Project
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package com.android.server.notification;
20 import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
21 import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
22 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
23 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
24 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
25 import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
26 import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
27 import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
28 import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
29 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
30 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
31 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
32 import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
33 import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
34 import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
35 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
36 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
37 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
38 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
39 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
40 import static android.service.notification.NotificationListenerService.TRIM_FULL;
41 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
42 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
43 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
45 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
47 import android.Manifest;
48 import android.annotation.Nullable;
49 import android.app.ActivityManager;
50 import android.app.ActivityManagerInternal;
51 import android.app.ActivityManagerNative;
52 import android.app.AppGlobals;
53 import android.app.AppOpsManager;
54 import android.app.AutomaticZenRule;
55 import android.app.IActivityManager;
56 import android.app.INotificationManager;
57 import android.app.ITransientNotification;
58 import android.app.KeyguardManager;
59 import android.app.Notification;
60 import android.app.NotificationManager;
61 import android.app.NotificationManager.Policy;
62 import android.app.PendingIntent;
63 import android.app.RemoteInput;
64 import android.app.StatusBarManager;
65 import android.app.backup.BackupManager;
66 import android.app.usage.UsageEvents;
67 import android.app.usage.UsageStatsManagerInternal;
68 import android.content.BroadcastReceiver;
69 import android.content.ComponentName;
70 import android.content.ContentResolver;
71 import android.content.Context;
72 import android.content.Intent;
73 import android.content.IntentFilter;
74 import android.content.pm.ApplicationInfo;
75 import android.content.pm.IPackageManager;
76 import android.content.pm.PackageInfo;
77 import android.content.pm.PackageManager;
78 import android.content.pm.PackageManager.NameNotFoundException;
79 import android.content.pm.ParceledListSlice;
80 import android.content.pm.UserInfo;
81 import android.content.res.Resources;
82 import android.database.ContentObserver;
83 import android.graphics.drawable.Drawable;
84 import android.media.AudioAttributes;
85 import android.media.AudioManager;
86 import android.media.AudioManagerInternal;
87 import android.media.AudioSystem;
88 import android.media.IRingtonePlayer;
89 import android.net.Uri;
90 import android.os.Binder;
91 import android.os.Bundle;
92 import android.os.Environment;
93 import android.os.Handler;
94 import android.os.HandlerThread;
95 import android.os.IBinder;
96 import android.os.IInterface;
97 import android.os.Looper;
98 import android.os.Message;
99 import android.os.Parcelable;
100 import android.os.PersistableBundle;
101 import android.os.Process;
102 import android.os.RemoteException;
103 import android.os.SystemClock;
104 import android.os.SystemProperties;
105 import android.os.UserHandle;
106 import android.os.UserManager;
107 import android.os.Vibrator;
108 import android.provider.Settings;
109 import android.service.notification.Adjustment;
110 import android.service.notification.Condition;
111 import android.service.notification.IConditionProvider;
112 import android.service.notification.INotificationListener;
113 import android.service.notification.IStatusBarNotificationHolder;
114 import android.service.notification.NotificationRankerService;
115 import android.service.notification.NotificationListenerService;
116 import android.service.notification.NotificationRankingUpdate;
117 import android.service.notification.StatusBarNotification;
118 import android.service.notification.ZenModeConfig;
119 import android.telephony.CarrierConfigManager;
120 import android.telephony.PhoneStateListener;
121 import android.telephony.TelephonyManager;
122 import android.text.TextUtils;
123 import android.util.ArrayMap;
124 import android.util.ArraySet;
125 import android.util.AtomicFile;
126 import android.util.Log;
127 import android.util.Slog;
128 import android.util.SparseArray;
129 import android.util.Xml;
130 import android.view.accessibility.AccessibilityEvent;
131 import android.view.accessibility.AccessibilityManager;
132 import android.widget.Toast;
134 import com.android.internal.R;
135 import com.android.internal.annotations.VisibleForTesting;
136 import com.android.internal.statusbar.NotificationVisibility;
137 import com.android.internal.util.FastXmlSerializer;
138 import com.android.internal.util.Preconditions;
139 import com.android.server.DeviceIdleController;
140 import com.android.server.EventLogTags;
141 import com.android.server.LocalServices;
142 import com.android.server.SystemService;
143 import com.android.server.lights.Light;
144 import com.android.server.lights.LightsManager;
145 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
146 import com.android.server.statusbar.StatusBarManagerInternal;
147 import com.android.server.vr.VrManagerInternal;
148 import com.android.server.notification.ManagedServices.UserProfiles;
150 import cyanogenmod.providers.CMSettings;
151 import cyanogenmod.util.ColorUtils;
153 import cyanogenmod.providers.CMSettings;
154 import libcore.io.IoUtils;
156 import org.json.JSONException;
157 import org.json.JSONObject;
158 import org.xmlpull.v1.XmlPullParser;
159 import org.xmlpull.v1.XmlPullParserException;
160 import org.xmlpull.v1.XmlSerializer;
162 import java.io.ByteArrayInputStream;
163 import java.io.ByteArrayOutputStream;
165 import java.io.FileDescriptor;
166 import java.io.FileInputStream;
167 import java.io.FileNotFoundException;
168 import java.io.FileOutputStream;
169 import java.io.IOException;
170 import java.io.InputStream;
171 import java.io.OutputStream;
172 import java.io.PrintWriter;
173 import java.nio.charset.StandardCharsets;
174 import java.util.ArrayDeque;
175 import java.util.ArrayList;
176 import java.util.Arrays;
177 import java.util.Iterator;
178 import java.util.List;
179 import java.util.Map;
180 import java.util.Map.Entry;
181 import java.util.Set;
182 import java.util.concurrent.TimeUnit;
185 public class NotificationManagerService extends SystemService {
186 static final String TAG = "NotificationService";
187 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
188 public static final boolean ENABLE_CHILD_NOTIFICATIONS
189 = SystemProperties.getBoolean("debug.child_notifs", true);
191 static final int MAX_PACKAGE_NOTIFICATIONS = 50;
192 static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 50f;
195 static final int MESSAGE_TIMEOUT = 2;
196 static final int MESSAGE_SAVE_POLICY_FILE = 3;
197 static final int MESSAGE_SEND_RANKING_UPDATE = 4;
198 static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
199 static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
201 // ranking thread messages
202 private static final int MESSAGE_RECONSIDER_RANKING = 1000;
203 private static final int MESSAGE_RANKING_SORT = 1001;
205 static final int LONG_DELAY = 3500; // 3.5 seconds
206 static final int SHORT_DELAY = 2000; // 2 seconds
208 static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
210 static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
212 static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
214 static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
215 static final boolean ENABLE_BLOCKED_TOASTS = true;
217 // When #matchesCallFilter is called from the ringer, wait at most
218 // 3s to resolve the contacts. This timeout is required since
219 // ContactsProvider might take a long time to start up.
221 // Return STARRED_CONTACT when the timeout is hit in order to avoid
222 // missed calls in ZEN mode "Important".
223 static final int MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS = 3000;
224 static final float MATCHES_CALL_FILTER_TIMEOUT_AFFINITY =
225 ValidateNotificationPeople.STARRED_CONTACT;
227 /** notification_enqueue status value for a newly enqueued notification. */
228 private static final int EVENTLOG_ENQUEUE_STATUS_NEW = 0;
230 /** notification_enqueue status value for an existing notification. */
231 private static final int EVENTLOG_ENQUEUE_STATUS_UPDATE = 1;
233 /** notification_enqueue status value for an ignored notification. */
234 private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
235 private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
236 private String mRankerServicePackageName;
238 /** notification light maximum brightness value to use. */
239 private static final int LIGHT_BRIGHTNESS_MAXIMUM = 255;
241 private IActivityManager mAm;
242 AudioManager mAudioManager;
243 AudioManagerInternal mAudioManagerInternal;
244 @Nullable StatusBarManagerInternal mStatusBar;
246 private VrManagerInternal mVrManagerInternal;
248 final IBinder mForegroundToken = new Binder();
249 private Handler mHandler;
250 private final HandlerThread mRankingThread = new HandlerThread("ranker",
251 Process.THREAD_PRIORITY_BACKGROUND);
253 private Light mNotificationLight;
254 Light mAttentionLight;
255 private int mDefaultNotificationColor;
256 private int mDefaultNotificationLedOn;
258 private int mDefaultNotificationLedOff;
259 private long[] mDefaultVibrationPattern;
261 private boolean mAdjustableNotificationLedBrightness;
262 private int mNotificationLedBrightnessLevel = LIGHT_BRIGHTNESS_MAXIMUM;
264 private boolean mMultipleNotificationLeds;
265 private boolean mMultipleLedsEnabledSetting = false;
267 private boolean mAutoGenerateNotificationColor = true;
269 private boolean mScreenOnEnabled = false;
270 private boolean mScreenOnDefault = false;
272 private long[] mFallbackVibrationPattern;
273 private boolean mUseAttentionLight;
274 boolean mSystemReady;
276 private boolean mDisableNotificationEffects;
277 private int mCallState;
278 private String mSoundNotificationKey;
279 private String mVibrateNotificationKey;
281 private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
282 new SparseArray<ArraySet<ManagedServiceInfo>>();
283 private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
284 private int mListenerHints; // right now, all hints are global
285 private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
287 // for enabling and disabling notification pulse behavior
288 private boolean mScreenOn = true;
289 private boolean mInCall = false;
290 private boolean mNotificationPulseEnabled;
291 private ArrayMap<String, NotificationLedValues> mNotificationPulseCustomLedValues;
292 private Map<String, String> mPackageNameMappings;
293 private final ArrayMap<String, Integer> mGeneratedPackageLedColors =
294 new ArrayMap<String, Integer>();
296 // for checking lockscreen status
297 private KeyguardManager mKeyguardManager;
299 // used as a mutex for access to all active notifications & listeners
300 final ArrayList<NotificationRecord> mNotificationList =
301 new ArrayList<NotificationRecord>();
302 final ArrayMap<String, NotificationRecord> mNotificationsByKey =
303 new ArrayMap<String, NotificationRecord>();
304 final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
305 final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
306 final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
307 final PolicyAccess mPolicyAccess = new PolicyAccess();
309 // The last key in this list owns the hardware.
310 ArrayList<String> mLights = new ArrayList<>();
312 private AppOpsManager mAppOps;
313 private UsageStatsManagerInternal mAppUsageStats;
315 private Archive mArchive;
317 // Persistent storage for notification policy
318 private AtomicFile mPolicyFile;
320 private static final int DB_VERSION = 1;
322 private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
323 private static final String ATTR_VERSION = "version";
325 private RankingHelper mRankingHelper;
327 private final UserProfiles mUserProfiles = new UserProfiles();
328 private NotificationListeners mListeners;
329 private NotificationRankers mRankerServices;
330 private ConditionProviders mConditionProviders;
331 private NotificationUsageStats mUsageStats;
333 private boolean mMultiColorNotificationLed;
335 private static final int MY_UID = Process.myUid();
336 private static final int MY_PID = Process.myPid();
337 private RankingHandler mRankingHandler;
338 private long mLastOverRateLogTime;
339 private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
340 private PersistableBundle mCarrierConfig;
341 private CarrierConfigManager mConfigManager;
343 private static class Archive {
344 final int mBufferSize;
345 final ArrayDeque<StatusBarNotification> mBuffer;
347 public Archive(int size) {
349 mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
352 public String toString() {
353 final StringBuilder sb = new StringBuilder();
354 final int N = mBuffer.size();
355 sb.append("Archive (");
357 sb.append(" notification");
358 sb.append((N==1)?")":"s)");
359 return sb.toString();
362 public void record(StatusBarNotification nr) {
363 if (mBuffer.size() == mBufferSize) {
364 mBuffer.removeFirst();
367 // We don't want to store the heavy bits of the notification in the archive,
368 // but other clients in the system process might be using the object, so we
369 // store a (lightened) copy.
370 mBuffer.addLast(nr.cloneLight());
373 public Iterator<StatusBarNotification> descendingIterator() {
374 return mBuffer.descendingIterator();
377 public StatusBarNotification[] getArray(int count) {
378 if (count == 0) count = mBufferSize;
379 final StatusBarNotification[] a
380 = new StatusBarNotification[Math.min(count, mBuffer.size())];
381 Iterator<StatusBarNotification> iter = descendingIterator();
383 while (iter.hasNext() && i < count) {
384 a[i++] = iter.next();
391 private void readPolicyXml(InputStream stream, boolean forRestore)
392 throws XmlPullParserException, NumberFormatException, IOException {
393 final XmlPullParser parser = Xml.newPullParser();
394 parser.setInput(stream, StandardCharsets.UTF_8.name());
396 while (parser.next() != END_DOCUMENT) {
397 mZenModeHelper.readXml(parser, forRestore);
398 mRankingHelper.readXml(parser, forRestore);
402 private void loadPolicyFile() {
403 if (DBG) Slog.d(TAG, "loadPolicyFile");
404 synchronized(mPolicyFile) {
406 FileInputStream infile = null;
408 infile = mPolicyFile.openRead();
409 readPolicyXml(infile, false /*forRestore*/);
410 } catch (FileNotFoundException e) {
412 } catch (IOException e) {
413 Log.wtf(TAG, "Unable to read notification policy", e);
414 } catch (NumberFormatException e) {
415 Log.wtf(TAG, "Unable to parse notification policy", e);
416 } catch (XmlPullParserException e) {
417 Log.wtf(TAG, "Unable to parse notification policy", e);
419 IoUtils.closeQuietly(infile);
424 public void savePolicyFile() {
425 mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
426 mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
429 private void handleSavePolicyFile() {
430 if (DBG) Slog.d(TAG, "handleSavePolicyFile");
431 synchronized (mPolicyFile) {
432 final FileOutputStream stream;
434 stream = mPolicyFile.startWrite();
435 } catch (IOException e) {
436 Slog.w(TAG, "Failed to save policy file", e);
441 writePolicyXml(stream, false /*forBackup*/);
442 mPolicyFile.finishWrite(stream);
443 } catch (IOException e) {
444 Slog.w(TAG, "Failed to save policy file, restoring backup", e);
445 mPolicyFile.failWrite(stream);
448 BackupManager.dataChanged(getContext().getPackageName());
451 private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
452 final XmlSerializer out = new FastXmlSerializer();
453 out.setOutput(stream, StandardCharsets.UTF_8.name());
454 out.startDocument(null, true);
455 out.startTag(null, TAG_NOTIFICATION_POLICY);
456 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
457 mZenModeHelper.writeXml(out, forBackup);
458 mRankingHelper.writeXml(out, forBackup);
459 out.endTag(null, TAG_NOTIFICATION_POLICY);
463 /** Use this when you actually want to post a notification or toast.
465 * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
467 private boolean noteNotificationOp(String pkg, int uid) {
468 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
469 != AppOpsManager.MODE_ALLOWED) {
470 Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
476 /** Use this to check if a package can post a notification or toast. */
477 private boolean checkNotificationOp(String pkg, int uid) {
478 return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
479 == AppOpsManager.MODE_ALLOWED && !isPackageSuspendedForUser(pkg, uid);
482 private static final class ToastRecord
486 final ITransientNotification callback;
489 ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
493 this.callback = callback;
494 this.duration = duration;
497 void update(int duration) {
498 this.duration = duration;
501 void dump(PrintWriter pw, String prefix, DumpFilter filter) {
502 if (filter != null && !filter.matches(pkg)) return;
503 pw.println(prefix + this);
507 public final String toString()
509 return "ToastRecord{"
510 + Integer.toHexString(System.identityHashCode(this))
512 + " callback=" + callback
513 + " duration=" + duration;
517 class NotificationLedValues {
523 private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
526 public void onSetDisabled(int status) {
527 synchronized (mNotificationList) {
528 mDisableNotificationEffects =
529 (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
530 if (disableNotificationEffects(null) != null) {
531 // cancel whatever's going on
532 long identity = Binder.clearCallingIdentity();
534 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
535 if (player != null) {
538 } catch (RemoteException e) {
540 Binder.restoreCallingIdentity(identity);
543 identity = Binder.clearCallingIdentity();
547 Binder.restoreCallingIdentity(identity);
554 public void onClearAll(int callingUid, int callingPid, int userId) {
555 synchronized (mNotificationList) {
556 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
557 /*includeCurrentProfiles*/ true);
562 public void onNotificationClick(int callingUid, int callingPid, String key) {
563 synchronized (mNotificationList) {
564 NotificationRecord r = mNotificationsByKey.get(key);
566 Log.w(TAG, "No notification with key: " + key);
569 final long now = System.currentTimeMillis();
570 EventLogTags.writeNotificationClicked(key,
571 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
573 StatusBarNotification sbn = r.sbn;
574 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
575 sbn.getId(), Notification.FLAG_AUTO_CANCEL,
576 Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
577 REASON_DELEGATE_CLICK, null);
582 public void onNotificationActionClick(int callingUid, int callingPid, String key,
584 synchronized (mNotificationList) {
585 NotificationRecord r = mNotificationsByKey.get(key);
587 Log.w(TAG, "No notification with key: " + key);
590 final long now = System.currentTimeMillis();
591 EventLogTags.writeNotificationActionClicked(key, actionIndex,
592 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
593 // TODO: Log action click via UsageStats.
598 public void onNotificationClear(int callingUid, int callingPid,
599 String pkg, String tag, int id, int userId) {
600 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
601 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
602 true, userId, REASON_DELEGATE_CANCEL, null);
606 public void onPanelRevealed(boolean clearEffects, int items) {
607 EventLogTags.writeNotificationPanelRevealed(items);
614 public void onPanelHidden() {
615 EventLogTags.writeNotificationPanelHidden();
619 public void clearEffects() {
620 synchronized (mNotificationList) {
621 if (DBG) Slog.d(TAG, "clearEffects");
623 clearVibrateLocked();
629 public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
630 int uid, int initialPid, String message, int userId) {
631 Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
632 + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
633 cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
634 REASON_DELEGATE_ERROR, null);
635 long ident = Binder.clearCallingIdentity();
637 ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
638 "Bad notification posted from package " + pkg
640 } catch (RemoteException e) {
642 Binder.restoreCallingIdentity(ident);
647 public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
648 NotificationVisibility[] noLongerVisibleKeys) {
649 synchronized (mNotificationList) {
650 for (NotificationVisibility nv : newlyVisibleKeys) {
651 NotificationRecord r = mNotificationsByKey.get(nv.key);
652 if (r == null) continue;
653 r.setVisibility(true, nv.rank);
656 // Note that we might receive this event after notifications
657 // have already left the system, e.g. after dismissing from the
658 // shade. Hence not finding notifications in
659 // mNotificationsByKey is not an exceptional condition.
660 for (NotificationVisibility nv : noLongerVisibleKeys) {
661 NotificationRecord r = mNotificationsByKey.get(nv.key);
662 if (r == null) continue;
663 r.setVisibility(false, nv.rank);
670 public void onNotificationExpansionChanged(String key,
671 boolean userAction, boolean expanded) {
672 synchronized (mNotificationList) {
673 NotificationRecord r = mNotificationsByKey.get(key);
675 r.stats.onExpansionChanged(userAction, expanded);
676 final long now = System.currentTimeMillis();
677 EventLogTags.writeNotificationExpansion(key,
678 userAction ? 1 : 0, expanded ? 1 : 0,
679 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
685 private void clearSoundLocked() {
686 mSoundNotificationKey = null;
687 long identity = Binder.clearCallingIdentity();
689 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
690 if (player != null) {
693 } catch (RemoteException e) {
695 Binder.restoreCallingIdentity(identity);
699 private void clearVibrateLocked() {
700 mVibrateNotificationKey = null;
701 long identity = Binder.clearCallingIdentity();
705 Binder.restoreCallingIdentity(identity);
709 private void clearLightsLocked() {
711 // clear only if lockscreen is not active
712 if (mKeyguardManager != null && !mKeyguardManager.isKeyguardLocked()) {
714 updateLightsLocked();
718 private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
720 public void onReceive(Context context, Intent intent) {
721 String action = intent.getAction();
722 if (action == null) {
726 boolean queryRestart = false;
727 boolean queryRemove = false;
728 boolean packageChanged = false;
729 boolean cancelNotifications = true;
730 int reason = REASON_PACKAGE_CHANGED;
732 if (action.equals(Intent.ACTION_PACKAGE_ADDED)
733 || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
734 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
735 || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
736 || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
737 || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
738 || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
739 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
740 UserHandle.USER_ALL);
741 String pkgList[] = null;
742 boolean queryReplace = queryRemove &&
743 intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
744 if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
745 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
746 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
747 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
748 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
749 reason = REASON_PACKAGE_SUSPENDED;
750 } else if (queryRestart) {
751 pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
753 Uri uri = intent.getData();
757 String pkgName = uri.getSchemeSpecificPart();
758 if (pkgName == null) {
761 if (packageChanged) {
762 // We cancel notifications for packages which have just been disabled
764 final IPackageManager pm = AppGlobals.getPackageManager();
765 final int enabled = pm.getApplicationEnabledSetting(pkgName,
766 changeUserId != UserHandle.USER_ALL ? changeUserId :
767 UserHandle.USER_SYSTEM);
768 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
769 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
770 cancelNotifications = false;
772 } catch (IllegalArgumentException e) {
773 // Package doesn't exist; probably racing with uninstall.
774 // cancelNotifications is already true, so nothing to do here.
776 Slog.i(TAG, "Exception trying to look up app enabled setting", e);
778 } catch (RemoteException e) {
779 // Failed to talk to PackageManagerService Should never happen!
782 pkgList = new String[]{pkgName};
785 if (pkgList != null && (pkgList.length > 0)) {
786 for (String pkgName : pkgList) {
787 if (cancelNotifications) {
788 cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
789 changeUserId, reason, null);
793 mListeners.onPackagesChanged(queryReplace, pkgList);
794 mRankerServices.onPackagesChanged(queryReplace, pkgList);
795 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
796 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
801 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
803 public void onReceive(Context context, Intent intent) {
804 String action = intent.getAction();
806 if (action.equals(Intent.ACTION_SCREEN_ON)) {
807 // Keep track of screen on/off state, but do not turn off the notification light
808 // until user passes through the lock screen or views the notification.
810 updateNotificationPulse();
811 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
813 updateNotificationPulse();
814 } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
815 mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
816 .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
817 updateNotificationPulse();
818 } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
819 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
820 if (userHandle >= 0) {
821 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
822 REASON_USER_STOPPED, null);
824 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
825 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
826 if (userHandle >= 0) {
827 cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
828 REASON_PROFILE_TURNED_OFF, null);
830 } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
831 // turn off LED when user passes through lock screen
832 // if lights with screen on is disabled.
833 if (!mScreenOnEnabled) {
834 mNotificationLight.turnOff();
836 if (mStatusBar != null) {
837 mStatusBar.notificationLightOff();
840 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
841 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
842 // reload per-user settings
843 mSettingsObserver.update(null);
844 mUserProfiles.updateCache(context);
845 // Refresh managed services
846 mConditionProviders.onUserSwitched(user);
847 mListeners.onUserSwitched(user);
848 mRankerServices.onUserSwitched(user);
849 mZenModeHelper.onUserSwitched(user);
850 } else if (action.equals(Intent.ACTION_USER_ADDED)) {
851 mUserProfiles.updateCache(context);
852 } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
853 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
854 mZenModeHelper.onUserRemoved(user);
855 } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
856 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
857 mConditionProviders.onUserUnlocked(user);
858 mListeners.onUserUnlocked(user);
859 mRankerServices.onUserUnlocked(user);
860 mZenModeHelper.onUserUnlocked(user);
865 private final class LEDSettingsObserver extends ContentObserver {
866 private final Uri NOTIFICATION_LIGHT_PULSE_URI
867 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
868 private final Uri NOTIFICATION_RATE_LIMIT_URI
869 = Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
870 private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
871 = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
873 LEDSettingsObserver(Handler handler) {
878 ContentResolver resolver = getContext().getContentResolver();
879 resolver.registerContentObserver(
880 NOTIFICATION_LIGHT_PULSE_URI, false, this, UserHandle.USER_ALL);
881 resolver.registerContentObserver(
882 ENABLED_NOTIFICATION_LISTENERS_URI, false, this, UserHandle.USER_ALL);
883 resolver.registerContentObserver(CMSettings.System.getUriFor(
884 CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR),
885 false, this, UserHandle.USER_ALL);
886 resolver.registerContentObserver(CMSettings.System.getUriFor(
887 CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON),
888 false, this, UserHandle.USER_ALL);
889 resolver.registerContentObserver(CMSettings.System.getUriFor(
890 CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF),
891 false, this, UserHandle.USER_ALL);
892 resolver.registerContentObserver(CMSettings.System.getUriFor(
893 CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE),
894 false, this, UserHandle.USER_ALL);
895 resolver.registerContentObserver(CMSettings.System.getUriFor(
896 CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES),
897 false, this, UserHandle.USER_ALL);
898 resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
899 false, this, UserHandle.USER_ALL);
900 resolver.registerContentObserver(CMSettings.System.getUriFor(
901 CMSettings.System.NOTIFICATION_LIGHT_SCREEN_ON),
902 false, this, UserHandle.USER_ALL);
903 resolver.registerContentObserver(CMSettings.System.getUriFor(
904 CMSettings.System.NOTIFICATION_LIGHT_COLOR_AUTO), false,
905 this, UserHandle.USER_ALL);
906 if (mAdjustableNotificationLedBrightness) {
907 resolver.registerContentObserver(CMSettings.System.getUriFor(
908 CMSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL),
909 false, this, UserHandle.USER_ALL);
911 if (mMultipleNotificationLeds) {
912 resolver.registerContentObserver(CMSettings.System.getUriFor(
913 CMSettings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE),
914 false, this, UserHandle.USER_ALL);
919 @Override public void onChange(boolean selfChange, Uri uri) {
923 public void update(Uri uri) {
924 ContentResolver resolver = getContext().getContentResolver();
927 mNotificationPulseEnabled = Settings.System.getIntForUser(resolver,
928 Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
930 // Automatically pick a color for LED if not set
931 mAutoGenerateNotificationColor = CMSettings.System.getIntForUser(resolver,
932 CMSettings.System.NOTIFICATION_LIGHT_COLOR_AUTO,
933 1, UserHandle.USER_CURRENT) != 0;
936 mDefaultNotificationColor = CMSettings.System.getIntForUser(resolver,
937 CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR,
938 mDefaultNotificationColor, UserHandle.USER_CURRENT);
941 mDefaultNotificationLedOn = CMSettings.System.getIntForUser(resolver,
942 CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON,
943 mDefaultNotificationLedOn, UserHandle.USER_CURRENT);
945 // LED default off MS
946 mDefaultNotificationLedOff = CMSettings.System.getIntForUser(resolver,
947 CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF,
948 mDefaultNotificationLedOff, UserHandle.USER_CURRENT);
950 // LED generated notification colors
951 mGeneratedPackageLedColors.clear();
953 // LED custom notification colors
954 mNotificationPulseCustomLedValues.clear();
955 if (CMSettings.System.getIntForUser(resolver,
956 CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0,
957 UserHandle.USER_CURRENT) != 0) {
958 parseNotificationPulseCustomValuesString(CMSettings.System.getStringForUser(resolver,
959 CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES,
960 UserHandle.USER_CURRENT));
962 if (uri == null || NOTIFICATION_RATE_LIMIT_URI.equals(uri)) {
963 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
964 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
967 // Notification LED brightness
968 if (mAdjustableNotificationLedBrightness) {
969 mNotificationLedBrightnessLevel = CMSettings.System.getIntForUser(resolver,
970 CMSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL,
971 LIGHT_BRIGHTNESS_MAXIMUM, UserHandle.USER_CURRENT);
974 // Multiple LEDs enabled
975 if (mMultipleNotificationLeds) {
976 mMultipleLedsEnabledSetting = (CMSettings.System.getIntForUser(resolver,
977 CMSettings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE,
978 mMultipleNotificationLeds ? 1 : 0, UserHandle.USER_CURRENT) != 0);
981 // Notification lights with screen on
982 mScreenOnEnabled = (CMSettings.System.getIntForUser(resolver,
983 CMSettings.System.NOTIFICATION_LIGHT_SCREEN_ON,
984 mScreenOnDefault ? 1 : 0, UserHandle.USER_CURRENT) != 0);
986 updateNotificationPulse();
990 private LEDSettingsObserver mSettingsObserver;
991 private ZenModeHelper mZenModeHelper;
993 private final Runnable mBuzzBeepBlinked = new Runnable() {
996 if (mStatusBar != null) {
997 mStatusBar.buzzBeepBlinked();
1002 static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
1003 int[] ar = r.getIntArray(resid);
1007 final int len = ar.length > maxlen ? maxlen : ar.length;
1008 long[] out = new long[len];
1009 for (int i=0; i<len; i++) {
1015 public NotificationManagerService(Context context) {
1020 void setAudioManager(AudioManager audioMananger) {
1021 mAudioManager = audioMananger;
1025 void setVibrator(Vibrator vibrator) {
1026 mVibrator = vibrator;
1030 void setSystemReady(boolean systemReady) {
1031 mSystemReady = systemReady;
1035 void setHandler(Handler handler) {
1040 public void onStart() {
1041 Resources resources = getContext().getResources();
1043 mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
1044 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
1045 DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
1047 mAm = ActivityManagerNative.getDefault();
1048 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
1049 mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
1050 mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1052 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
1054 // This is the package that contains the AOSP framework update.
1055 mRankerServicePackageName = getContext().getPackageManager()
1056 .getServicesSystemSharedLibraryPackageName();
1058 mHandler = new WorkerHandler();
1059 mRankingThread.start();
1060 String[] extractorNames;
1062 extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
1063 } catch (Resources.NotFoundException e) {
1064 extractorNames = new String[0];
1066 mUsageStats = new NotificationUsageStats(getContext());
1067 mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
1068 mRankingHelper = new RankingHelper(getContext(),
1072 mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
1073 mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
1074 mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
1076 public void onConfigChanged() {
1081 void onZenModeChanged() {
1082 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1083 getContext().sendBroadcastAsUser(
1084 new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
1085 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
1086 UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
1087 synchronized(mNotificationList) {
1088 updateInterruptionFilterLocked();
1093 void onPolicyChanged() {
1094 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
1097 final File systemDir = new File(Environment.getDataDirectory(), "system");
1098 mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
1102 // This is a MangedServices object that keeps track of the listeners.
1103 mListeners = new NotificationListeners();
1105 // This is a MangedServices object that keeps track of the ranker.
1106 mRankerServices = new NotificationRankers();
1107 // Find the updatable ranker and register it.
1108 mRankerServices.registerRanker();
1110 mStatusBar = getLocalService(StatusBarManagerInternal.class);
1111 if (mStatusBar != null) {
1112 mStatusBar.setNotificationDelegate(mNotificationDelegate);
1115 final LightsManager lights = getLocalService(LightsManager.class);
1116 mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
1117 mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
1119 mDefaultNotificationColor = resources.getColor(
1120 R.color.config_defaultNotificationColor);
1121 mDefaultNotificationLedOn = resources.getInteger(
1122 R.integer.config_defaultNotificationLedOn);
1123 mDefaultNotificationLedOff = resources.getInteger(
1124 R.integer.config_defaultNotificationLedOff);
1126 mMultiColorNotificationLed = resources.getBoolean(
1127 R.bool.config_multiColorNotificationLed);
1129 mNotificationPulseCustomLedValues = new ArrayMap<String, NotificationLedValues>();
1131 mPackageNameMappings = new ArrayMap<String, String>();
1132 final String[] defaultMapping = resources.getStringArray(
1133 com.android.internal.R.array.notification_light_package_mapping);
1134 for (String mapping : defaultMapping) {
1135 String[] map = mapping.split("\\|");
1136 mPackageNameMappings.put(map[0], map[1]);
1139 mDefaultVibrationPattern = getLongArray(resources,
1140 R.array.config_defaultNotificationVibePattern,
1141 VIBRATE_PATTERN_MAXLEN,
1142 DEFAULT_VIBRATE_PATTERN);
1144 mFallbackVibrationPattern = getLongArray(resources,
1145 R.array.config_notificationFallbackVibePattern,
1146 VIBRATE_PATTERN_MAXLEN,
1147 DEFAULT_VIBRATE_PATTERN);
1149 mAdjustableNotificationLedBrightness = resources.getBoolean(
1150 org.cyanogenmod.platform.internal.R.bool.config_adjustableNotificationLedBrightness);
1151 mMultipleNotificationLeds = resources.getBoolean(
1152 org.cyanogenmod.platform.internal.R.bool.config_multipleNotificationLeds);
1154 mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
1156 // Don't start allowing notifications until the setup wizard has run once.
1157 // After that, including subsequent boots, init with notifications turned on.
1158 // This works on the first boot because the setup wizard will toggle this
1159 // flag at least once and we'll go back to 0 after that.
1160 if (0 == Settings.Global.getInt(getContext().getContentResolver(),
1161 Settings.Global.DEVICE_PROVISIONED, 0)) {
1162 mDisableNotificationEffects = true;
1164 mZenModeHelper.initZenMode();
1165 mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1167 mUserProfiles.updateCache(getContext());
1168 listenForCallState();
1170 // register for various Intents
1171 IntentFilter filter = new IntentFilter();
1172 filter.addAction(Intent.ACTION_SCREEN_ON);
1173 filter.addAction(Intent.ACTION_SCREEN_OFF);
1174 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
1175 filter.addAction(Intent.ACTION_USER_PRESENT);
1176 filter.addAction(Intent.ACTION_USER_STOPPED);
1177 filter.addAction(Intent.ACTION_USER_SWITCHED);
1178 filter.addAction(Intent.ACTION_USER_ADDED);
1179 filter.addAction(Intent.ACTION_USER_REMOVED);
1180 filter.addAction(Intent.ACTION_USER_UNLOCKED);
1181 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
1182 getContext().registerReceiver(mIntentReceiver, filter);
1184 IntentFilter pkgFilter = new IntentFilter();
1185 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
1186 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1187 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1188 pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1189 pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1190 pkgFilter.addDataScheme("package");
1191 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
1194 IntentFilter suspendedPkgFilter = new IntentFilter();
1195 suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
1196 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
1197 suspendedPkgFilter, null, null);
1199 IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1200 getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
1203 mSettingsObserver = new LEDSettingsObserver(mHandler);
1204 mSettingsObserver.observe();
1206 mArchive = new Archive(resources.getInteger(
1207 R.integer.config_notificationServiceArchiveSize));
1209 publishBinderService(Context.NOTIFICATION_SERVICE, mService);
1210 publishLocalService(NotificationManagerInternal.class, mInternalService);
1211 mConfigManager = (CarrierConfigManager)
1212 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1215 private void sendRegisteredOnlyBroadcast(String action) {
1216 getContext().sendBroadcastAsUser(new Intent(action)
1217 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
1221 * Make sure the XML config and the the AppOps system agree about blocks.
1223 private void syncBlockDb() {
1226 // sync bans from ranker into app opps
1227 Map<Integer, String> packageBans = mRankingHelper.getPackageBans();
1228 for(Entry<Integer, String> ban : packageBans.entrySet()) {
1229 final int uid = ban.getKey();
1230 final String packageName = ban.getValue();
1231 setNotificationsEnabledForPackageImpl(packageName, uid, false);
1234 // sync bans from app opps into ranker
1235 packageBans.clear();
1236 for (UserInfo user : UserManager.get(getContext()).getUsers()) {
1237 final int userId = user.getUserHandle().getIdentifier();
1238 final PackageManager packageManager = getContext().getPackageManager();
1239 List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
1240 final int packageCount = packages.size();
1241 for (int p = 0; p < packageCount; p++) {
1242 final String packageName = packages.get(p).packageName;
1244 final int uid = packageManager.getPackageUidAsUser(packageName, userId);
1245 if (!checkNotificationOp(packageName, uid)) {
1246 packageBans.put(uid, packageName);
1248 } catch (NameNotFoundException e) {
1253 for (Entry<Integer, String> ban : packageBans.entrySet()) {
1254 mRankingHelper.setImportance(ban.getValue(), ban.getKey(), IMPORTANCE_NONE);
1261 public void onBootPhase(int phase) {
1262 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1263 // no beeping until we're basically done booting
1264 mSystemReady = true;
1266 // Grab our optional AudioService
1267 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1268 mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
1269 mVrManagerInternal = getLocalService(VrManagerInternal.class);
1270 mZenModeHelper.onSystemReady();
1271 } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
1272 // This observer will force an update when observe is called, causing us to
1273 // bind to listener services.
1274 mSettingsObserver.observe();
1275 mListeners.onBootPhaseAppsCanStart();
1276 mRankerServices.onBootPhaseAppsCanStart();
1277 mConditionProviders.onBootPhaseAppsCanStart();
1281 void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) {
1282 Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
1284 mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
1285 enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
1287 // Now, cancel any outstanding notifications that are part of a just-disabled app
1288 if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
1289 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
1290 REASON_PACKAGE_BANNED, null);
1294 private void updateListenerHintsLocked() {
1295 final int hints = calculateHints();
1296 if (hints == mListenerHints) return;
1297 ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
1298 mListenerHints = hints;
1299 scheduleListenerHintsChanged(hints);
1302 private void updateEffectsSuppressorLocked() {
1303 final long updatedSuppressedEffects = calculateSuppressedEffects();
1304 if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
1305 final List<ComponentName> suppressors = getSuppressors();
1306 ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
1307 mEffectsSuppressors = suppressors;
1308 mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
1309 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1312 private ArrayList<ComponentName> getSuppressors() {
1313 ArrayList<ComponentName> names = new ArrayList<ComponentName>();
1314 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1315 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1317 for (ManagedServiceInfo info : serviceInfoList) {
1318 names.add(info.component);
1325 private boolean removeDisabledHints(ManagedServiceInfo info) {
1326 return removeDisabledHints(info, 0);
1329 private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
1330 boolean removed = false;
1332 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1333 final int hint = mListenersDisablingEffects.keyAt(i);
1334 final ArraySet<ManagedServiceInfo> listeners =
1335 mListenersDisablingEffects.valueAt(i);
1337 if (hints == 0 || (hint & hints) == hint) {
1338 removed = removed || listeners.remove(info);
1345 private void addDisabledHints(ManagedServiceInfo info, int hints) {
1346 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1347 addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
1350 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1351 addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
1354 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1355 addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
1359 private void addDisabledHint(ManagedServiceInfo info, int hint) {
1360 if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
1361 mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
1364 ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
1365 hintListeners.add(info);
1368 private int calculateHints() {
1370 for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
1371 int hint = mListenersDisablingEffects.keyAt(i);
1372 ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
1374 if (!serviceInfoList.isEmpty()) {
1382 private long calculateSuppressedEffects() {
1383 int hints = calculateHints();
1384 long suppressedEffects = 0;
1386 if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
1387 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
1390 if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
1391 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
1394 if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
1395 suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
1398 return suppressedEffects;
1401 private void updateInterruptionFilterLocked() {
1402 int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
1403 if (interruptionFilter == mInterruptionFilter) return;
1404 mInterruptionFilter = interruptionFilter;
1405 scheduleInterruptionFilterChanged(interruptionFilter);
1408 private final IBinder mService = new INotificationManager.Stub() {
1410 // ============================================================================
1413 public void enqueueToast(String pkg, ITransientNotification callback, int duration)
1416 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
1417 + " duration=" + duration);
1420 if (pkg == null || callback == null) {
1421 Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
1425 final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
1426 final boolean isPackageSuspended =
1427 isPackageSuspendedForUser(pkg, Binder.getCallingUid());
1429 if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
1430 || isPackageSuspended)) {
1431 if (!isSystemToast) {
1432 Slog.e(TAG, "Suppressing toast from package " + pkg
1433 + (isPackageSuspended
1434 ? " due to package suspended by administrator."
1435 : " by user request."));
1440 synchronized (mToastQueue) {
1441 int callingPid = Binder.getCallingPid();
1442 long callingId = Binder.clearCallingIdentity();
1445 int index = indexOfToastLocked(pkg, callback);
1446 // If it's already in the queue, we update it in place, we don't
1447 // move it to the end of the queue.
1449 record = mToastQueue.get(index);
1450 record.update(duration);
1452 // Limit the number of toasts that any given package except the android
1453 // package can enqueue. Prevents DOS attacks and deals with leaks.
1454 if (!isSystemToast) {
1456 final int N = mToastQueue.size();
1457 for (int i=0; i<N; i++) {
1458 final ToastRecord r = mToastQueue.get(i);
1459 if (r.pkg.equals(pkg)) {
1461 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
1462 Slog.e(TAG, "Package has already posted " + count
1463 + " toasts. Not showing more. Package=" + pkg);
1470 record = new ToastRecord(callingPid, pkg, callback, duration);
1471 mToastQueue.add(record);
1472 index = mToastQueue.size() - 1;
1473 keepProcessAliveLocked(callingPid);
1475 // If it's at index 0, it's the current toast. It doesn't matter if it's
1476 // new or just been updated. Call back and tell it to show itself.
1477 // If the callback fails, this will remove it from the list, so don't
1478 // assume that it's valid after this.
1480 showNextToastLocked();
1483 Binder.restoreCallingIdentity(callingId);
1489 public void cancelToast(String pkg, ITransientNotification callback) {
1490 Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
1492 if (pkg == null || callback == null) {
1493 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
1497 synchronized (mToastQueue) {
1498 long callingId = Binder.clearCallingIdentity();
1500 int index = indexOfToastLocked(pkg, callback);
1502 cancelToastLocked(index);
1504 Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
1505 + " callback=" + callback);
1508 Binder.restoreCallingIdentity(callingId);
1514 public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
1515 Notification notification, int[] idOut, int userId) throws RemoteException {
1516 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
1517 Binder.getCallingPid(), tag, id, notification, idOut, userId);
1521 public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
1522 checkCallerIsSystemOrSameApp(pkg);
1523 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1524 Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
1525 // Don't allow client applications to cancel foreground service notis or autobundled
1527 cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
1528 (Binder.getCallingUid() == Process.SYSTEM_UID
1529 ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
1530 | (Binder.getCallingUid() == Process.SYSTEM_UID
1531 ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
1532 REASON_APP_CANCEL, null);
1536 public void cancelAllNotifications(String pkg, int userId) {
1537 checkCallerIsSystemOrSameApp(pkg);
1539 userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1540 Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
1542 // Calling from user space, don't allow the canceling of actively
1543 // running foreground services.
1544 cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
1545 pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
1546 REASON_APP_CANCEL_ALL, null);
1550 public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
1551 checkCallerIsSystem();
1553 setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
1554 mRankingHelper.setEnabled(pkg, uid, enabled);
1559 * Use this when you just want to know if notifications are OK for this package.
1562 public boolean areNotificationsEnabled(String pkg) {
1563 return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
1567 * Use this when you just want to know if notifications are OK for this package.
1570 public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
1571 checkCallerIsSystemOrSameApp(pkg);
1572 return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
1573 == AppOpsManager.MODE_ALLOWED) && !isPackageSuspendedForUser(pkg, uid);
1577 public void setPriority(String pkg, int uid, int priority) {
1578 checkCallerIsSystem();
1579 mRankingHelper.setPriority(pkg, uid, priority);
1584 public int getPriority(String pkg, int uid) {
1585 checkCallerIsSystem();
1586 return mRankingHelper.getPriority(pkg, uid);
1590 public void setVisibilityOverride(String pkg, int uid, int visibility) {
1591 checkCallerIsSystem();
1592 mRankingHelper.setVisibilityOverride(pkg, uid, visibility);
1597 public int getVisibilityOverride(String pkg, int uid) {
1598 checkCallerIsSystem();
1599 return mRankingHelper.getVisibilityOverride(pkg, uid);
1603 public void setImportance(String pkg, int uid, int importance) {
1604 enforceSystemOrSystemUI("Caller not system or systemui");
1605 setNotificationsEnabledForPackageImpl(pkg, uid,
1606 importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
1607 mRankingHelper.setImportance(pkg, uid, importance);
1612 public int getPackageImportance(String pkg) {
1613 checkCallerIsSystemOrSameApp(pkg);
1614 return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
1618 public int getImportance(String pkg, int uid) {
1619 enforceSystemOrSystemUI("Caller not system or systemui");
1620 return mRankingHelper.getImportance(pkg, uid);
1624 * System-only API for getting a list of current (i.e. not cleared) notifications.
1626 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1627 * @returns A list of all the notifications, in natural order.
1630 public StatusBarNotification[] getActiveNotifications(String callingPkg) {
1631 // enforce() will ensure the calling uid has the correct permission
1632 getContext().enforceCallingOrSelfPermission(
1633 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1634 "NotificationManagerService.getActiveNotifications");
1636 StatusBarNotification[] tmp = null;
1637 int uid = Binder.getCallingUid();
1639 // noteOp will check to make sure the callingPkg matches the uid
1640 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1641 == AppOpsManager.MODE_ALLOWED) {
1642 synchronized (mNotificationList) {
1643 tmp = new StatusBarNotification[mNotificationList.size()];
1644 final int N = mNotificationList.size();
1645 for (int i=0; i<N; i++) {
1646 tmp[i] = mNotificationList.get(i).sbn;
1654 * Public API for getting a list of current notifications for the calling package/uid.
1656 * @returns A list of all the package's notifications, in natural order.
1659 public ParceledListSlice<StatusBarNotification> getAppActiveNotifications(String pkg,
1660 int incomingUserId) {
1661 checkCallerIsSystemOrSameApp(pkg);
1662 int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
1663 Binder.getCallingUid(), incomingUserId, true, false,
1664 "getAppActiveNotifications", pkg);
1666 final ArrayList<StatusBarNotification> list
1667 = new ArrayList<StatusBarNotification>(mNotificationList.size());
1669 synchronized (mNotificationList) {
1670 final int N = mNotificationList.size();
1671 for (int i = 0; i < N; i++) {
1672 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
1673 if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
1674 && (sbn.getNotification().flags
1675 & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
1676 // We could pass back a cloneLight() but clients might get confused and
1677 // try to send this thing back to notify() again, which would not work
1679 final StatusBarNotification sbnOut = new StatusBarNotification(
1680 sbn.getPackageName(),
1682 sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
1683 0, // hide score from apps
1684 sbn.getNotification().clone(),
1685 sbn.getUser(), sbn.getPostTime());
1691 return new ParceledListSlice<StatusBarNotification>(list);
1695 * System-only API for getting a list of recent (cleared, no longer shown) notifications.
1697 * Requires ACCESS_NOTIFICATIONS which is signature|system.
1700 public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
1701 // enforce() will ensure the calling uid has the correct permission
1702 getContext().enforceCallingOrSelfPermission(
1703 android.Manifest.permission.ACCESS_NOTIFICATIONS,
1704 "NotificationManagerService.getHistoricalNotifications");
1706 StatusBarNotification[] tmp = null;
1707 int uid = Binder.getCallingUid();
1709 // noteOp will check to make sure the callingPkg matches the uid
1710 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
1711 == AppOpsManager.MODE_ALLOWED) {
1712 synchronized (mArchive) {
1713 tmp = mArchive.getArray(count);
1720 * Register a listener binder directly with the notification manager.
1722 * Only works with system callers. Apps should extend
1723 * {@link android.service.notification.NotificationListenerService}.
1726 public void registerListener(final INotificationListener listener,
1727 final ComponentName component, final int userid) {
1728 enforceSystemOrSystemUI("INotificationManager.registerListener");
1729 mListeners.registerService(listener, component, userid);
1733 * Remove a listener binder directly
1736 public void unregisterListener(INotificationListener token, int userid) {
1737 mListeners.unregisterService(token, userid);
1741 * Allow an INotificationListener to simulate a "clear all" operation.
1743 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
1745 * @param token The binder for the listener, to check that the caller is allowed
1748 public void cancelNotificationsFromListener(INotificationListener token, String[] keys) {
1749 final int callingUid = Binder.getCallingUid();
1750 final int callingPid = Binder.getCallingPid();
1751 long identity = Binder.clearCallingIdentity();
1753 synchronized (mNotificationList) {
1754 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1756 final int N = keys.length;
1757 for (int i = 0; i < N; i++) {
1758 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1759 if (r == null) continue;
1760 final int userId = r.sbn.getUserId();
1761 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1762 !mUserProfiles.isCurrentProfile(userId)) {
1763 throw new SecurityException("Disallowed call from listener: "
1766 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1767 r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
1771 cancelAllLocked(callingUid, callingPid, info.userid,
1772 REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
1776 Binder.restoreCallingIdentity(identity);
1781 * Handle request from an approved listener to re-enable itself.
1783 * @param component The componenet to be re-enabled, caller must match package.
1786 public void requestBindListener(ComponentName component) {
1787 checkCallerIsSystemOrSameApp(component.getPackageName());
1788 long identity = Binder.clearCallingIdentity();
1790 ManagedServices manager =
1791 mRankerServices.isComponentEnabledForCurrentProfiles(component)
1794 manager.setComponentState(component, true);
1796 Binder.restoreCallingIdentity(identity);
1801 public void requestUnbindListener(INotificationListener token) {
1802 long identity = Binder.clearCallingIdentity();
1804 // allow bound services to disable themselves
1805 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1806 info.getOwner().setComponentState(info.component, false);
1808 Binder.restoreCallingIdentity(identity);
1813 public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
1814 long identity = Binder.clearCallingIdentity();
1816 synchronized (mNotificationList) {
1817 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1819 final int N = keys.length;
1820 for (int i = 0; i < N; i++) {
1821 NotificationRecord r = mNotificationsByKey.get(keys[i]);
1822 if (r == null) continue;
1823 final int userId = r.sbn.getUserId();
1824 if (userId != info.userid && userId != UserHandle.USER_ALL &&
1825 !mUserProfiles.isCurrentProfile(userId)) {
1826 throw new SecurityException("Disallowed call from listener: "
1830 if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
1831 mAppUsageStats.reportEvent(r.sbn.getPackageName(),
1832 userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
1834 UsageEvents.Event.USER_INTERACTION);
1841 Binder.restoreCallingIdentity(identity);
1845 private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
1846 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
1847 cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
1848 Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
1850 userId, REASON_LISTENER_CANCEL, info);
1854 * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
1856 * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
1858 * @param token The binder for the listener, to check that the caller is allowed
1861 public void cancelNotificationFromListener(INotificationListener token, String pkg,
1862 String tag, int id) {
1863 final int callingUid = Binder.getCallingUid();
1864 final int callingPid = Binder.getCallingPid();
1865 long identity = Binder.clearCallingIdentity();
1867 synchronized (mNotificationList) {
1868 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1869 if (info.supportsProfiles()) {
1870 Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
1871 + "from " + info.component
1872 + " use cancelNotification(key) instead.");
1874 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
1875 pkg, tag, id, info.userid);
1879 Binder.restoreCallingIdentity(identity);
1884 * Allow an INotificationListener to request the list of outstanding notifications seen by
1885 * the current user. Useful when starting up, after which point the listener callbacks
1888 * @param token The binder for the listener, to check that the caller is allowed
1889 * @param keys An array of notification keys to fetch, or null to fetch everything
1890 * @returns The return value will contain the notifications specified in keys, in that
1891 * order, or if keys is null, all the notifications, in natural order.
1894 public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
1895 INotificationListener token, String[] keys, int trim) {
1896 synchronized (mNotificationList) {
1897 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1898 final boolean getKeys = keys != null;
1899 final int N = getKeys ? keys.length : mNotificationList.size();
1900 final ArrayList<StatusBarNotification> list
1901 = new ArrayList<StatusBarNotification>(N);
1902 for (int i=0; i<N; i++) {
1903 final NotificationRecord r = getKeys
1904 ? mNotificationsByKey.get(keys[i])
1905 : mNotificationList.get(i);
1906 if (r == null) continue;
1907 StatusBarNotification sbn = r.sbn;
1908 if (!isVisibleToListener(sbn, info)) continue;
1909 StatusBarNotification sbnToSend =
1910 (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
1911 list.add(sbnToSend);
1913 return new ParceledListSlice<StatusBarNotification>(list);
1918 public void requestHintsFromListener(INotificationListener token, int hints) {
1919 final long identity = Binder.clearCallingIdentity();
1921 synchronized (mNotificationList) {
1922 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1923 final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
1924 | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
1925 | HINT_HOST_DISABLE_CALL_EFFECTS;
1926 final boolean disableEffects = (hints & disableEffectsMask) != 0;
1927 if (disableEffects) {
1928 addDisabledHints(info, hints);
1930 removeDisabledHints(info, hints);
1932 updateListenerHintsLocked();
1933 updateEffectsSuppressorLocked();
1936 Binder.restoreCallingIdentity(identity);
1941 public int getHintsFromListener(INotificationListener token) {
1942 synchronized (mNotificationList) {
1943 return mListenerHints;
1948 public void requestInterruptionFilterFromListener(INotificationListener token,
1949 int interruptionFilter) throws RemoteException {
1950 final long identity = Binder.clearCallingIdentity();
1952 synchronized (mNotificationList) {
1953 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1954 mZenModeHelper.requestFromListener(info.component, interruptionFilter);
1955 updateInterruptionFilterLocked();
1958 Binder.restoreCallingIdentity(identity);
1963 public int getInterruptionFilterFromListener(INotificationListener token)
1964 throws RemoteException {
1965 synchronized (mNotificationLight) {
1966 return mInterruptionFilter;
1971 public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
1972 throws RemoteException {
1973 synchronized (mNotificationList) {
1974 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
1975 if (info == null) return;
1976 mListeners.setOnNotificationPostedTrimLocked(info, trim);
1981 public int getZenMode() {
1982 return mZenModeHelper.getZenMode();
1986 public ZenModeConfig getZenModeConfig() {
1987 enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig");
1988 return mZenModeHelper.getConfig();
1992 public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
1993 enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
1994 final long identity = Binder.clearCallingIdentity();
1996 mZenModeHelper.setManualZenMode(mode, conditionId, reason);
1998 Binder.restoreCallingIdentity(identity);
2003 public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
2004 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
2005 return mZenModeHelper.getZenRules();
2009 public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
2010 Preconditions.checkNotNull(id, "Id is null");
2011 enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
2012 return mZenModeHelper.getAutomaticZenRule(id);
2016 public String addAutomaticZenRule(AutomaticZenRule automaticZenRule)
2017 throws RemoteException {
2018 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2019 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2020 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2021 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2022 enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
2024 return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
2025 "addAutomaticZenRule");
2029 public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
2030 throws RemoteException {
2031 Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
2032 Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
2033 Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
2034 Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
2035 enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
2037 return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
2038 "updateAutomaticZenRule");
2042 public boolean removeAutomaticZenRule(String id) throws RemoteException {
2043 Preconditions.checkNotNull(id, "Id is null");
2044 // Verify that they can modify zen rules.
2045 enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
2047 return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
2051 public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
2052 Preconditions.checkNotNull(packageName, "Package name is null");
2053 enforceSystemOrSystemUI("removeAutomaticZenRules");
2055 return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
2059 public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
2060 Preconditions.checkNotNull(owner, "Owner is null");
2061 enforceSystemOrSystemUI("getRuleInstanceCount");
2063 return mZenModeHelper.getCurrentInstanceCount(owner);
2067 public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
2068 enforcePolicyAccess(pkg, "setInterruptionFilter");
2069 final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
2070 if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
2071 final long identity = Binder.clearCallingIdentity();
2073 mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
2075 Binder.restoreCallingIdentity(identity);
2080 public void notifyConditions(final String pkg, IConditionProvider provider,
2081 final Condition[] conditions) {
2082 final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
2083 checkCallerIsSystemOrSameApp(pkg);
2084 mHandler.post(new Runnable() {
2087 mConditionProviders.notifyConditions(pkg, info, conditions);
2092 private void enforceSystemOrSystemUIOrVolume(String message) {
2093 if (mAudioManagerInternal != null) {
2094 final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
2095 if (vcuid > 0 && Binder.getCallingUid() == vcuid) {
2099 enforceSystemOrSystemUI(message);
2102 private void enforceSystemOrSystemUI(String message) {
2103 if (isCallerSystem()) return;
2104 getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
2108 private void enforceSystemOrSystemUIOrSamePackage(String pkg, String message) {
2110 checkCallerIsSystemOrSameApp(pkg);
2111 } catch (SecurityException e) {
2112 getContext().enforceCallingPermission(
2113 android.Manifest.permission.STATUS_BAR_SERVICE,
2118 private void enforcePolicyAccess(int uid, String method) {
2119 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2120 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2123 boolean accessAllowed = false;
2124 String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
2125 final int packageCount = packages.length;
2126 for (int i = 0; i < packageCount; i++) {
2127 if (checkPolicyAccess(packages[i])) {
2128 accessAllowed = true;
2131 if (!accessAllowed) {
2132 Slog.w(TAG, "Notification policy access denied calling " + method);
2133 throw new SecurityException("Notification policy access denied");
2137 private void enforcePolicyAccess(String pkg, String method) {
2138 if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
2139 android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
2142 checkCallerIsSameApp(pkg);
2143 if (!checkPolicyAccess(pkg)) {
2144 Slog.w(TAG, "Notification policy access denied calling " + method);
2145 throw new SecurityException("Notification policy access denied");
2149 private boolean checkPackagePolicyAccess(String pkg) {
2150 return mPolicyAccess.isPackageGranted(pkg);
2153 private boolean checkPolicyAccess(String pkg) {
2155 int uid = getContext().getPackageManager().getPackageUidAsUser(
2156 pkg, UserHandle.getCallingUserId());
2157 if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
2158 android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
2162 } catch (NameNotFoundException e) {
2165 return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
2169 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2170 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2171 != PackageManager.PERMISSION_GRANTED) {
2172 pw.println("Permission Denial: can't dump NotificationManager from pid="
2173 + Binder.getCallingPid()
2174 + ", uid=" + Binder.getCallingUid());
2178 final DumpFilter filter = DumpFilter.parseFromArguments(args);
2179 if (filter != null && filter.stats) {
2180 dumpJson(pw, filter);
2182 dumpImpl(pw, filter);
2187 public ComponentName getEffectsSuppressor() {
2188 enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
2189 return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
2193 public boolean matchesCallFilter(Bundle extras) {
2194 enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
2195 return mZenModeHelper.matchesCallFilter(
2196 Binder.getCallingUserHandle(),
2198 mRankingHelper.findExtractor(ValidateNotificationPeople.class),
2199 MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
2200 MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
2204 public boolean isSystemConditionProviderEnabled(String path) {
2205 enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
2206 return mConditionProviders.isSystemProviderEnabled(path);
2209 // Backup/restore interface
2211 public byte[] getBackupPayload(int user) {
2212 if (DBG) Slog.d(TAG, "getBackupPayload u=" + user);
2213 //TODO: http://b/22388012
2214 if (user != UserHandle.USER_SYSTEM) {
2215 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
2218 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
2220 writePolicyXml(baos, true /*forBackup*/);
2221 return baos.toByteArray();
2222 } catch (IOException e) {
2223 Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
2229 public void applyRestore(byte[] payload, int user) {
2230 if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload="
2231 + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null));
2232 if (payload == null) {
2233 Slog.w(TAG, "applyRestore: no payload to restore for user " + user);
2236 //TODO: http://b/22388012
2237 if (user != UserHandle.USER_SYSTEM) {
2238 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
2241 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
2243 readPolicyXml(bais, true /*forRestore*/);
2245 } catch (NumberFormatException | XmlPullParserException | IOException e) {
2246 Slog.w(TAG, "applyRestore: error reading payload", e);
2251 public boolean isNotificationPolicyAccessGranted(String pkg) {
2252 return checkPolicyAccess(pkg);
2256 public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {;
2257 enforceSystemOrSystemUIOrSamePackage(pkg,
2258 "request policy access status for another package");
2259 return checkPolicyAccess(pkg);
2263 public String[] getPackagesRequestingNotificationPolicyAccess()
2264 throws RemoteException {
2265 enforceSystemOrSystemUI("request policy access packages");
2266 final long identity = Binder.clearCallingIdentity();
2268 return mPolicyAccess.getRequestingPackages();
2270 Binder.restoreCallingIdentity(identity);
2275 public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
2276 throws RemoteException {
2277 enforceSystemOrSystemUI("grant notification policy access");
2278 final long identity = Binder.clearCallingIdentity();
2280 synchronized (mNotificationList) {
2281 mPolicyAccess.put(pkg, granted);
2284 Binder.restoreCallingIdentity(identity);
2289 public Policy getNotificationPolicy(String pkg) {
2290 enforcePolicyAccess(pkg, "getNotificationPolicy");
2291 final long identity = Binder.clearCallingIdentity();
2293 return mZenModeHelper.getNotificationPolicy();
2295 Binder.restoreCallingIdentity(identity);
2300 public void setNotificationPolicy(String pkg, Policy policy) {
2301 enforcePolicyAccess(pkg, "setNotificationPolicy");
2302 final long identity = Binder.clearCallingIdentity();
2304 mZenModeHelper.setNotificationPolicy(policy);
2306 Binder.restoreCallingIdentity(identity);
2311 public void applyAdjustmentFromRankerService(INotificationListener token,
2312 Adjustment adjustment) throws RemoteException {
2313 final long identity = Binder.clearCallingIdentity();
2315 synchronized (mNotificationList) {
2316 mRankerServices.checkServiceTokenLocked(token);
2317 applyAdjustmentLocked(adjustment);
2319 maybeAddAutobundleSummary(adjustment);
2320 mRankingHandler.requestSort();
2322 Binder.restoreCallingIdentity(identity);
2327 public void applyAdjustmentsFromRankerService(INotificationListener token,
2328 List<Adjustment> adjustments) throws RemoteException {
2330 final long identity = Binder.clearCallingIdentity();
2332 synchronized (mNotificationList) {
2333 mRankerServices.checkServiceTokenLocked(token);
2334 for (Adjustment adjustment : adjustments) {
2335 applyAdjustmentLocked(adjustment);
2338 for (Adjustment adjustment : adjustments) {
2339 maybeAddAutobundleSummary(adjustment);
2341 mRankingHandler.requestSort();
2343 Binder.restoreCallingIdentity(identity);
2348 private void applyAdjustmentLocked(Adjustment adjustment) {
2349 maybeClearAutobundleSummaryLocked(adjustment);
2350 NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
2354 if (adjustment.getImportance() != IMPORTANCE_NONE) {
2355 n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
2357 if (adjustment.getSignals() != null) {
2358 Bundle.setDefusable(adjustment.getSignals(), true);
2359 final String autoGroupKey = adjustment.getSignals().getString(
2360 Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2361 if (autoGroupKey == null) {
2362 EventLogTags.writeNotificationUnautogrouped(adjustment.getKey());
2364 EventLogTags.writeNotificationAutogrouped(adjustment.getKey());
2366 n.sbn.setOverrideGroupKey(autoGroupKey);
2370 // Clears the 'fake' auto-bunding summary.
2371 private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
2372 if (adjustment.getSignals() != null) {
2373 Bundle.setDefusable(adjustment.getSignals(), true);
2374 if (adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
2375 && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2376 ArrayMap<String, String> summaries =
2377 mAutobundledSummaries.get(adjustment.getUser());
2378 if (summaries != null && summaries.containsKey(adjustment.getPackage())) {
2380 final NotificationRecord removed = mNotificationsByKey.get(
2381 summaries.remove(adjustment.getPackage()));
2382 if (removed != null) {
2383 mNotificationList.remove(removed);
2384 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
2391 // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
2392 private void maybeAddAutobundleSummary(Adjustment adjustment) {
2393 if (adjustment.getSignals() != null) {
2394 Bundle.setDefusable(adjustment.getSignals(), true);
2395 if (adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
2396 final String newAutoBundleKey =
2397 adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
2399 NotificationRecord summaryRecord = null;
2400 synchronized (mNotificationList) {
2401 NotificationRecord notificationRecord =
2402 mNotificationsByKey.get(adjustment.getKey());
2403 if (notificationRecord == null) {
2404 // The notification could have been cancelled again already. A successive
2405 // adjustment will post a summary if needed.
2408 final StatusBarNotification adjustedSbn = notificationRecord.sbn;
2409 userId = adjustedSbn.getUser().getIdentifier();
2410 ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
2411 if (summaries == null) {
2412 summaries = new ArrayMap<>();
2414 mAutobundledSummaries.put(userId, summaries);
2415 if (!summaries.containsKey(adjustment.getPackage())
2416 && newAutoBundleKey != null) {
2418 final ApplicationInfo appInfo =
2419 adjustedSbn.getNotification().extras.getParcelable(
2420 Notification.EXTRA_BUILDER_APPLICATION_INFO);
2421 final Bundle extras = new Bundle();
2422 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
2423 final Notification summaryNotification =
2424 new Notification.Builder(getContext()).setSmallIcon(
2425 adjustedSbn.getNotification().getSmallIcon())
2426 .setGroupSummary(true)
2427 .setGroup(newAutoBundleKey)
2428 .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
2429 .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
2430 .setColor(adjustedSbn.getNotification().color)
2432 summaryNotification.extras.putAll(extras);
2433 Intent appIntent = getContext().getPackageManager()
2434 .getLaunchIntentForPackage(adjustment.getPackage());
2435 if (appIntent != null) {
2436 summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
2437 getContext(), 0, appIntent, 0, null,
2438 UserHandle.of(userId));
2440 final StatusBarNotification summarySbn =
2441 new StatusBarNotification(adjustedSbn.getPackageName(),
2442 adjustedSbn.getOpPkg(),
2443 Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
2444 adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
2445 summaryNotification, adjustedSbn.getUser(),
2447 System.currentTimeMillis());
2448 summaryRecord = new NotificationRecord(getContext(), summarySbn);
2449 summaries.put(adjustment.getPackage(), summarySbn.getKey());
2452 if (summaryRecord != null) {
2453 mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
2459 private String disableNotificationEffects(NotificationRecord record) {
2460 if (mDisableNotificationEffects) {
2461 return "booleanState";
2463 if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
2464 return "listenerHints";
2466 if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
2472 private void dumpJson(PrintWriter pw, DumpFilter filter) {
2473 JSONObject dump = new JSONObject();
2475 dump.put("service", "Notification Manager");
2476 dump.put("bans", mRankingHelper.dumpBansJson(filter));
2477 dump.put("ranking", mRankingHelper.dumpJson(filter));
2478 dump.put("stats", mUsageStats.dumpJson(filter));
2479 } catch (JSONException e) {
2480 e.printStackTrace();
2485 void dumpImpl(PrintWriter pw, DumpFilter filter) {
2486 pw.print("Current Notification Manager state");
2487 if (filter.filtered) {
2488 pw.print(" (filtered to "); pw.print(filter); pw.print(")");
2492 final boolean zenOnly = filter.filtered && filter.zen;
2495 synchronized (mToastQueue) {
2496 N = mToastQueue.size();
2498 pw.println(" Toast Queue:");
2499 for (int i=0; i<N; i++) {
2500 mToastQueue.get(i).dump(pw, " ", filter);
2507 synchronized (mNotificationList) {
2509 N = mNotificationList.size();
2511 pw.println(" Notification List:");
2512 for (int i=0; i<N; i++) {
2513 final NotificationRecord nr = mNotificationList.get(i);
2514 if (filter.filtered && !filter.matches(nr.sbn)) continue;
2515 nr.dump(pw, " ", getContext(), filter.redact);
2520 if (!filter.filtered) {
2523 pw.println(" Lights List:");
2524 for (int i=0; i<N; i++) {
2530 pw.println(mLights.get(i));
2534 pw.println(" mUseAttentionLight=" + mUseAttentionLight);
2535 pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
2536 pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
2537 pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
2538 pw.println(" mDisableNotificationEffects=" + mDisableNotificationEffects);
2539 pw.println(" mCallState=" + callStateToString(mCallState));
2540 pw.println(" mSystemReady=" + mSystemReady);
2541 pw.println(" mMaxPackageEnqueueRate=" + mMaxPackageEnqueueRate);
2543 pw.println(" mArchive=" + mArchive.toString());
2544 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
2546 while (iter.hasNext()) {
2547 final StatusBarNotification sbn = iter.next();
2548 if (filter != null && !filter.matches(sbn)) continue;
2549 pw.println(" " + sbn);
2551 if (iter.hasNext()) pw.println(" ...");
2558 pw.println("\n Usage Stats:");
2559 mUsageStats.dump(pw, " ", filter);
2562 if (!filter.filtered || zenOnly) {
2563 pw.println("\n Zen Mode:");
2564 pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter);
2565 mZenModeHelper.dump(pw, " ");
2567 pw.println("\n Zen Log:");
2568 ZenLog.dump(pw, " ");
2572 pw.println("\n Ranking Config:");
2573 mRankingHelper.dump(pw, " ", filter);
2575 pw.println("\n Notification listeners:");
2576 mListeners.dump(pw, filter);
2577 pw.print(" mListenerHints: "); pw.println(mListenerHints);
2578 pw.print(" mListenersDisablingEffects: (");
2579 N = mListenersDisablingEffects.size();
2580 for (int i = 0; i < N; i++) {
2581 final int hint = mListenersDisablingEffects.keyAt(i);
2582 if (i > 0) pw.print(';');
2583 pw.print("hint[" + hint + "]:");
2585 final ArraySet<ManagedServiceInfo> listeners =
2586 mListenersDisablingEffects.valueAt(i);
2587 final int listenerSize = listeners.size();
2589 for (int j = 0; j < listenerSize; j++) {
2590 if (i > 0) pw.print(',');
2591 final ManagedServiceInfo listener = listeners.valueAt(i);
2592 pw.print(listener.component);
2596 pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
2597 pw.println("\n Notification ranker services:");
2598 mRankerServices.dump(pw, filter);
2600 pw.println("\n Policy access:");
2601 pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
2603 pw.println("\n Condition providers:");
2604 mConditionProviders.dump(pw, filter);
2606 pw.println("\n Group summaries:");
2607 for (Entry<String, NotificationRecord> entry : mSummaryByGroupKey.entrySet()) {
2608 NotificationRecord r = entry.getValue();
2609 pw.println(" " + entry.getKey() + " -> " + r.getKey());
2610 if (mNotificationsByKey.get(r.getKey()) != r) {
2611 pw.println("!!!!!!LEAK: Record not found in mNotificationsByKey.");
2612 r.dump(pw, " ", getContext(), filter.redact);
2619 * The private API only accessible to the system process.
2621 private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
2623 public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
2624 String tag, int id, Notification notification, int[] idReceived, int userId) {
2625 enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
2626 idReceived, userId);
2630 public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
2632 checkCallerIsSystem();
2633 synchronized (mNotificationList) {
2634 int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
2636 Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
2637 + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
2640 NotificationRecord r = mNotificationList.get(i);
2641 StatusBarNotification sbn = r.sbn;
2642 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
2643 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
2644 // we have to revert to the flags we received initially *and* force remove
2645 // FLAG_FOREGROUND_SERVICE.
2646 sbn.getNotification().flags =
2647 (r.mOriginalFlags & ~Notification.FLAG_FOREGROUND_SERVICE);
2648 mRankingHelper.sort(mNotificationList);
2649 mListeners.notifyPostedLocked(sbn, sbn /* oldSbn */);
2654 void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
2655 final int callingPid, final String tag, final int id, final Notification notification,
2656 int[] idOut, int incomingUserId) {
2658 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
2659 + " notification=" + notification);
2661 checkCallerIsSystemOrSameApp(pkg);
2662 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
2663 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
2665 final int userId = ActivityManager.handleIncomingUser(callingPid,
2666 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
2667 final UserHandle user = new UserHandle(userId);
2669 // Fix the notification as best we can.
2671 final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
2672 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2673 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
2674 Notification.addFieldsFromContext(ai, userId, notification);
2675 } catch (NameNotFoundException e) {
2676 Slog.e(TAG, "Cannot create a context for sending app", e);
2680 mUsageStats.registerEnqueuedByApp(pkg);
2682 // Limit the number of notifications that any given package except the android
2683 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
2684 if (!isSystemNotification && !isNotificationFromListener) {
2685 synchronized (mNotificationList) {
2686 final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
2687 if (appEnqueueRate > mMaxPackageEnqueueRate) {
2688 mUsageStats.registerOverRateQuota(pkg);
2689 final long now = SystemClock.elapsedRealtime();
2690 if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
2691 Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
2692 + ". Shedding events. package=" + pkg);
2693 mLastOverRateLogTime = now;
2699 final int N = mNotificationList.size();
2700 for (int i=0; i<N; i++) {
2701 final NotificationRecord r = mNotificationList.get(i);
2702 if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
2703 if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
2704 break; // Allow updating existing notification
2707 if (count >= MAX_PACKAGE_NOTIFICATIONS) {
2708 mUsageStats.registerOverCountQuota(pkg);
2709 Slog.e(TAG, "Package has already posted " + count
2710 + " notifications. Not showing more. package=" + pkg);
2718 if (pkg == null || notification == null) {
2719 throw new IllegalArgumentException("null not allowed: pkg=" + pkg
2720 + " id=" + id + " notification=" + notification);
2723 // Whitelist pending intents.
2724 if (notification.allPendingIntents != null) {
2725 final int intentCount = notification.allPendingIntents.size();
2726 if (intentCount > 0) {
2727 final ActivityManagerInternal am = LocalServices
2728 .getService(ActivityManagerInternal.class);
2729 final long duration = LocalServices.getService(
2730 DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
2731 for (int i = 0; i < intentCount; i++) {
2732 PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
2733 if (pendingIntent != null) {
2734 am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
2741 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
2742 Notification.PRIORITY_MAX);
2744 // setup local book-keeping
2745 final StatusBarNotification n = new StatusBarNotification(
2746 pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
2748 final NotificationRecord r = new NotificationRecord(getContext(), n);
2749 mHandler.post(new EnqueueNotificationRunnable(userId, r));
2754 private class EnqueueNotificationRunnable implements Runnable {
2755 private final NotificationRecord r;
2756 private final int userId;
2758 EnqueueNotificationRunnable(int userId, NotificationRecord r) {
2759 this.userId = userId;
2766 synchronized (mNotificationList) {
2767 final StatusBarNotification n = r.sbn;
2768 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
2769 NotificationRecord old = mNotificationsByKey.get(n.getKey());
2771 // Retain ranking information from previous record
2772 r.copyRankingInformation(old);
2775 final int callingUid = n.getUid();
2776 final int callingPid = n.getInitialPid();
2777 final Notification notification = n.getNotification();
2778 final String pkg = n.getPackageName();
2779 final int id = n.getId();
2780 final String tag = n.getTag();
2781 final boolean isSystemNotification = isUidSystem(callingUid) ||
2782 ("android".equals(pkg));
2784 // Handle grouped notifications and bail out early if we
2785 // can to avoid extracting signals.
2786 handleGroupedNotificationLocked(r, old, callingUid, callingPid);
2788 // This conditional is a dirty hack to limit the logging done on
2789 // behalf of the download manager without affecting other apps.
2790 if (!pkg.equals("com.android.providers.downloads")
2791 || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
2792 int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
2794 enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
2796 EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
2797 pkg, id, tag, userId, notification.toString(),
2801 mRankingHelper.extractSignals(r);
2803 final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
2806 if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
2807 || !noteNotificationOp(pkg, callingUid) || isPackageSuspended) {
2808 if (!isSystemNotification) {
2809 if (isPackageSuspended) {
2810 Slog.e(TAG, "Suppressing notification from package due to package "
2811 + "suspended by administrator.");
2812 mUsageStats.registerSuspendedByAdmin(r);
2814 Slog.e(TAG, "Suppressing notification from package by user request.");
2815 mUsageStats.registerBlocked(r);
2821 // tell the ranker service about the notification
2822 if (mRankerServices.isEnabled()) {
2823 mRankerServices.onNotificationEnqueued(r);
2824 // TODO delay the code below here for 100ms or until there is an answer
2828 int index = indexOfNotificationLocked(n.getKey());
2830 mNotificationList.add(r);
2831 mUsageStats.registerPostedByApp(r);
2833 old = mNotificationList.get(index);
2834 mNotificationList.set(index, r);
2835 mUsageStats.registerUpdatedByApp(r, old);
2836 // Make sure we don't lose the foreground service state.
2837 notification.flags |=
2838 old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
2842 mNotificationsByKey.put(n.getKey(), r);
2844 // Ensure if this is a foreground service that the proper additional
2846 if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2847 notification.flags |= Notification.FLAG_ONGOING_EVENT
2848 | Notification.FLAG_NO_CLEAR;
2851 applyZenModeLocked(r);
2852 mRankingHelper.sort(mNotificationList);
2854 if (notification.getSmallIcon() != null) {
2855 StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
2856 mListeners.notifyPostedLocked(n, oldSbn);
2858 Slog.e(TAG, "Not posting notification without small icon: " + notification);
2859 if (old != null && !old.isCanceled) {
2860 mListeners.notifyRemovedLocked(n);
2862 // ATTENTION: in a future release we will bail out here
2863 // so that we do not play sounds, show lights, etc. for invalid
2865 Slog.e(TAG, "WARNING: In a future release this will crash the app: "
2866 + n.getPackageName());
2869 buzzBeepBlinkLocked(r);
2875 * Ensures that grouped notification receive their special treatment.
2877 * <p>Cancels group children if the new notification causes a group to lose
2880 * <p>Updates mSummaryByGroupKey.</p>
2882 private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
2883 int callingUid, int callingPid) {
2884 StatusBarNotification sbn = r.sbn;
2885 Notification n = sbn.getNotification();
2886 if (n.isGroupSummary() && !sbn.isAppGroup()) {
2887 // notifications without a group shouldn't be a summary, otherwise autobundling can
2889 n.flags &= ~Notification.FLAG_GROUP_SUMMARY;
2892 String group = sbn.getGroupKey();
2893 boolean isSummary = n.isGroupSummary();
2895 Notification oldN = old != null ? old.sbn.getNotification() : null;
2896 String oldGroup = old != null ? old.sbn.getGroupKey() : null;
2897 boolean oldIsSummary = old != null && oldN.isGroupSummary();
2900 NotificationRecord removedSummary = mSummaryByGroupKey.remove(oldGroup);
2901 if (removedSummary != old) {
2903 removedSummary != null ? removedSummary.getKey() : "<null>";
2904 Slog.w(TAG, "Removed summary didn't match old notification: old=" + old.getKey() +
2905 ", removed=" + removedKey);
2909 mSummaryByGroupKey.put(group, r);
2912 // Clear out group children of the old notification if the update
2913 // causes the group summary to go away. This happens when the old
2914 // notification was a summary and the new one isn't, or when the old
2915 // notification was a summary and its group key changed.
2916 if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
2917 cancelGroupChildrenLocked(old, callingUid, callingPid, null,
2918 REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
2923 void buzzBeepBlinkLocked(NotificationRecord record) {
2924 boolean buzz = false;
2925 boolean beep = false;
2926 boolean blink = false;
2928 final Notification notification = record.sbn.getNotification();
2929 final String key = record.getKey();
2931 // Should this notification make noise, vibe, or use the LED?
2932 final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
2933 final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
2934 if (DBG || record.isIntercepted())
2936 "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
2937 " intercept=" + record.isIntercepted()
2940 final int currentUser;
2941 final long token = Binder.clearCallingIdentity();
2943 currentUser = ActivityManager.getCurrentUser();
2945 Binder.restoreCallingIdentity(token);
2948 // If we're not supposed to beep, vibrate, etc. then don't.
2949 final String disableEffects = disableNotificationEffects(record);
2950 if (disableEffects != null) {
2951 ZenLog.traceDisableEffects(record, disableEffects);
2954 // Remember if this notification already owns the notification channels.
2955 boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
2956 boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
2958 // These are set inside the conditional if the notification is allowed to make noise.
2959 boolean hasValidVibrate = false;
2960 boolean hasValidSound = false;
2962 boolean smsRingtone = false;
2963 if (mCarrierConfig == null) {
2964 mCarrierConfig = mConfigManager.getConfig();
2966 smsRingtone = mCarrierConfig.getBoolean(
2967 CarrierConfigManager.KEY_CONFIG_SMS_RINGTONE_INCALL);
2969 if ((disableEffects == null || (smsRingtone && mInCall))
2970 && (record.getUserId() == UserHandle.USER_ALL ||
2971 record.getUserId() == currentUser ||
2972 mUserProfiles.isCurrentProfile(record.getUserId()))
2975 && mAudioManager != null) {
2976 if (DBG) Slog.v(TAG, "Interrupting!");
2978 // should we use the default notification sound? (indicated either by
2979 // DEFAULT_SOUND or because notification.sound is pointing at
2980 // Settings.System.NOTIFICATION_SOUND)
2981 final boolean useDefaultSound =
2982 (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
2983 Settings.System.DEFAULT_NOTIFICATION_URI
2984 .equals(notification.sound);
2986 Uri soundUri = null;
2987 if (useDefaultSound) {
2988 soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
2990 // check to see if the default notification sound is silent
2991 ContentResolver resolver = getContext().getContentResolver();
2992 hasValidSound = Settings.System.getString(resolver,
2993 Settings.System.NOTIFICATION_SOUND) != null;
2994 } else if (notification.sound != null) {
2995 soundUri = notification.sound;
2996 hasValidSound = (soundUri != null);
2999 // Does the notification want to specify its own vibration?
3000 final boolean hasCustomVibrate = notification.vibrate != null;
3002 // new in 4.2: if there was supposed to be a sound and we're in vibrate
3003 // mode, and no other vibration is specified, we fall back to vibration
3004 final boolean convertSoundToVibration =
3007 && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
3009 // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
3010 final boolean useDefaultVibrate =
3011 (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
3013 hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
3016 // We can alert, and we're allowed to alert, but if the developer asked us to only do
3017 // it once, and we already have, then don't.
3018 if (!(record.isUpdate
3019 && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
3021 sendAccessibilityEvent(notification, record.sbn.getPackageName());
3023 if (hasValidSound) {
3025 (notification.flags & Notification.FLAG_INSISTENT) != 0;
3026 AudioAttributes audioAttributes = audioAttributesForNotification(notification);
3027 mSoundNotificationKey = key;
3028 // do not play notifications if stream volume is 0 (typically because
3029 // ringer mode is silent) or if there is a user of exclusive audio focus
3030 if ((mAudioManager.getStreamVolume(
3031 AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
3032 && !mAudioManager.isAudioFocusExclusive()) {
3033 final long identity = Binder.clearCallingIdentity();
3035 final IRingtonePlayer player =
3036 mAudioManager.getRingtonePlayer();
3037 if (player != null) {
3038 if (DBG) Slog.v(TAG, "Playing sound " + soundUri
3039 + " with attributes " + audioAttributes);
3040 player.playAsync(soundUri, record.sbn.getUser(), looping,
3044 } catch (RemoteException e) {
3046 Binder.restoreCallingIdentity(identity);
3051 if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
3052 == AudioManager.RINGER_MODE_SILENT)) {
3053 mVibrateNotificationKey = key;
3055 if (useDefaultVibrate || convertSoundToVibration) {
3056 // Escalate privileges so we can use the vibrator even if the
3057 // notifying app does not have the VIBRATE permission.
3058 long identity = Binder.clearCallingIdentity();
3060 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3061 useDefaultVibrate ? mDefaultVibrationPattern
3062 : mFallbackVibrationPattern,
3063 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
3064 ? 0: -1, audioAttributesForNotification(notification));
3067 Binder.restoreCallingIdentity(identity);
3069 } else if (notification.vibrate.length > 1) {
3070 // If you want your own vibration pattern, you need the VIBRATE
3072 mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
3073 notification.vibrate,
3074 ((notification.flags & Notification.FLAG_INSISTENT) != 0)
3075 ? 0: -1, audioAttributesForNotification(notification));
3082 // If a notification is updated to remove the actively playing sound or vibrate,
3083 // cancel that feedback now
3084 if (wasBeep && !hasValidSound) {
3087 if (wasBuzz && !hasValidVibrate) {
3088 clearVibrateLocked();
3092 // release the light
3093 boolean wasShowLights = mLights.remove(key);
3094 final boolean aboveThresholdWithLight = aboveThreshold || isLedNotificationForcedOn(record);
3095 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThresholdWithLight
3096 && ((record.getSuppressedVisualEffects()
3097 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
3099 updateLightsLocked();
3100 if (mUseAttentionLight) {
3101 mAttentionLight.pulse();
3104 } else if (wasShowLights) {
3105 updateLightsLocked();
3107 if (buzz || beep || blink) {
3108 if (((record.getSuppressedVisualEffects()
3109 & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
3110 if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
3112 EventLogTags.writeNotificationAlert(key,
3113 buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
3114 mHandler.post(mBuzzBeepBlinked);
3119 private static AudioAttributes audioAttributesForNotification(Notification n) {
3120 if (n.audioAttributes != null
3121 && !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {
3122 // the audio attributes are set and different from the default, use them
3123 return n.audioAttributes;
3124 } else if (n.audioStreamType >= 0 && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
3125 // the stream type is valid, use it
3126 return new AudioAttributes.Builder()
3127 .setInternalLegacyStreamType(n.audioStreamType)
3129 } else if (n.audioStreamType == AudioSystem.STREAM_DEFAULT) {
3130 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
3132 Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
3133 return Notification.AUDIO_ATTRIBUTES_DEFAULT;
3137 void showNextToastLocked() {
3138 ToastRecord record = mToastQueue.get(0);
3139 while (record != null) {
3140 if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
3142 record.callback.show();
3143 scheduleTimeoutLocked(record);
3145 } catch (RemoteException e) {
3146 Slog.w(TAG, "Object died trying to show notification " + record.callback
3147 + " in package " + record.pkg);
3148 // remove it from the list and let the process die
3149 int index = mToastQueue.indexOf(record);
3151 mToastQueue.remove(index);
3153 keepProcessAliveLocked(record.pid);
3154 if (mToastQueue.size() > 0) {
3155 record = mToastQueue.get(0);
3163 void cancelToastLocked(int index) {
3164 ToastRecord record = mToastQueue.get(index);
3166 record.callback.hide();
3167 } catch (RemoteException e) {
3168 Slog.w(TAG, "Object died trying to hide notification " + record.callback
3169 + " in package " + record.pkg);
3170 // don't worry about this, we're about to remove it from
3173 mToastQueue.remove(index);
3174 keepProcessAliveLocked(record.pid);
3175 if (mToastQueue.size() > 0) {
3176 // Show the next one. If the callback fails, this will remove
3177 // it from the list, so don't assume that the list hasn't changed
3178 // after this point.
3179 showNextToastLocked();
3183 private void scheduleTimeoutLocked(ToastRecord r)
3185 mHandler.removeCallbacksAndMessages(r);
3186 Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
3187 long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
3188 mHandler.sendMessageDelayed(m, delay);
3191 private void handleTimeout(ToastRecord record)
3193 if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
3194 synchronized (mToastQueue) {
3195 int index = indexOfToastLocked(record.pkg, record.callback);
3197 cancelToastLocked(index);
3202 // lock on mToastQueue
3203 int indexOfToastLocked(String pkg, ITransientNotification callback)
3205 IBinder cbak = callback.asBinder();
3206 ArrayList<ToastRecord> list = mToastQueue;
3207 int len = list.size();
3208 for (int i=0; i<len; i++) {
3209 ToastRecord r = list.get(i);
3210 if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
3217 // lock on mToastQueue
3218 void keepProcessAliveLocked(int pid)
3220 int toastCount = 0; // toasts from this pid
3221 ArrayList<ToastRecord> list = mToastQueue;
3222 int N = list.size();
3223 for (int i=0; i<N; i++) {
3224 ToastRecord r = list.get(i);
3230 mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
3231 } catch (RemoteException e) {
3232 // Shouldn't happen.
3236 private void handleRankingReconsideration(Message message) {
3237 if (!(message.obj instanceof RankingReconsideration)) return;
3238 RankingReconsideration recon = (RankingReconsideration) message.obj;
3241 synchronized (mNotificationList) {
3242 final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
3243 if (record == null) {
3246 int indexBefore = findNotificationRecordIndexLocked(record);
3247 boolean interceptBefore = record.isIntercepted();
3248 int visibilityBefore = record.getPackageVisibilityOverride();
3249 recon.applyChangesLocked(record);
3250 applyZenModeLocked(record);
3251 mRankingHelper.sort(mNotificationList);
3252 int indexAfter = findNotificationRecordIndexLocked(record);
3253 boolean interceptAfter = record.isIntercepted();
3254 int visibilityAfter = record.getPackageVisibilityOverride();
3255 changed = indexBefore != indexAfter || interceptBefore != interceptAfter
3256 || visibilityBefore != visibilityAfter;
3257 if (interceptBefore && !interceptAfter) {
3258 buzzBeepBlinkLocked(record);
3262 scheduleSendRankingUpdate();
3266 private void handleRankingSort() {
3267 synchronized (mNotificationList) {
3268 final int N = mNotificationList.size();
3269 ArrayList<String> orderBefore = new ArrayList<String>(N);
3270 ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
3271 int[] visibilities = new int[N];
3272 int[] importances = new int[N];
3273 for (int i = 0; i < N; i++) {
3274 final NotificationRecord r = mNotificationList.get(i);
3275 orderBefore.add(r.getKey());
3276 groupOverrideBefore.add(r.sbn.getGroupKey());
3277 visibilities[i] = r.getPackageVisibilityOverride();
3278 importances[i] = r.getImportance();
3279 mRankingHelper.extractSignals(r);
3281 mRankingHelper.sort(mNotificationList);
3282 for (int i = 0; i < N; i++) {
3283 final NotificationRecord r = mNotificationList.get(i);
3284 if (!orderBefore.get(i).equals(r.getKey())
3285 || visibilities[i] != r.getPackageVisibilityOverride()
3286 || importances[i] != r.getImportance()
3287 || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
3288 scheduleSendRankingUpdate();
3295 // let zen mode evaluate this record
3296 private void applyZenModeLocked(NotificationRecord record) {
3297 record.setIntercepted(mZenModeHelper.shouldIntercept(record));
3298 if (record.isIntercepted()) {
3299 int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
3300 ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
3301 | (mZenModeHelper.shouldSuppressWhenScreenOn()
3302 ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
3303 record.setSuppressedVisualEffects(suppressed);
3307 // lock on mNotificationList
3308 private int findNotificationRecordIndexLocked(NotificationRecord target) {
3309 return mRankingHelper.indexOf(mNotificationList, target);
3312 private void scheduleSendRankingUpdate() {
3313 if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
3314 Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
3315 mHandler.sendMessage(m);
3319 private void handleSendRankingUpdate() {
3320 synchronized (mNotificationList) {
3321 mListeners.notifyRankingUpdateLocked();
3325 private void scheduleListenerHintsChanged(int state) {
3326 mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
3327 mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
3330 private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
3331 mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
3332 mHandler.obtainMessage(
3333 MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
3334 listenerInterruptionFilter,
3338 private void handleListenerHintsChanged(int hints) {
3339 synchronized (mNotificationList) {
3340 mListeners.notifyListenerHintsChangedLocked(hints);
3344 private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
3345 synchronized (mNotificationList) {
3346 mListeners.notifyInterruptionFilterChanged(interruptionFilter);
3350 private final class WorkerHandler extends Handler
3353 public void handleMessage(Message msg)
3357 case MESSAGE_TIMEOUT:
3358 handleTimeout((ToastRecord)msg.obj);
3360 case MESSAGE_SAVE_POLICY_FILE:
3361 handleSavePolicyFile();
3363 case MESSAGE_SEND_RANKING_UPDATE:
3364 handleSendRankingUpdate();
3366 case MESSAGE_LISTENER_HINTS_CHANGED:
3367 handleListenerHintsChanged(msg.arg1);
3369 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
3370 handleListenerInterruptionFilterChanged(msg.arg1);
3377 private final class RankingHandlerWorker extends Handler implements RankingHandler
3379 public RankingHandlerWorker(Looper looper) {
3384 public void handleMessage(Message msg) {
3386 case MESSAGE_RECONSIDER_RANKING:
3387 handleRankingReconsideration(msg);
3389 case MESSAGE_RANKING_SORT:
3390 handleRankingSort();
3395 public void requestSort() {
3396 removeMessages(MESSAGE_RANKING_SORT);
3397 sendEmptyMessage(MESSAGE_RANKING_SORT);
3400 public void requestReconsideration(RankingReconsideration recon) {
3401 Message m = Message.obtain(this,
3402 NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
3403 long delay = recon.getDelay(TimeUnit.MILLISECONDS);
3404 sendMessageDelayed(m, delay);
3409 // ============================================================================
3410 static int clamp(int x, int low, int high) {
3411 return (x < low) ? low : ((x > high) ? high : x);
3414 void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
3415 AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
3416 if (!manager.isEnabled()) {
3420 AccessibilityEvent event =
3421 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
3422 event.setPackageName(packageName);
3423 event.setClassName(Notification.class.getName());
3424 event.setParcelableData(notification);
3425 CharSequence tickerText = notification.tickerText;
3426 if (!TextUtils.isEmpty(tickerText)) {
3427 event.getText().add(tickerText);
3430 manager.sendAccessibilityEvent(event);
3433 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
3436 if (r.getNotification().deleteIntent != null) {
3438 r.getNotification().deleteIntent.send();
3439 } catch (PendingIntent.CanceledException ex) {
3440 // do nothing - there's no relevant way to recover, and
3441 // no reason to let this propagate
3442 Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
3448 if (r.getNotification().getSmallIcon() != null) {
3449 r.isCanceled = true;
3450 mListeners.notifyRemovedLocked(r.sbn);
3453 final String canceledKey = r.getKey();
3456 if (canceledKey.equals(mSoundNotificationKey)) {
3457 mSoundNotificationKey = null;
3458 final long identity = Binder.clearCallingIdentity();
3460 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
3461 if (player != null) {
3464 } catch (RemoteException e) {
3466 Binder.restoreCallingIdentity(identity);
3471 if (canceledKey.equals(mVibrateNotificationKey)) {
3472 mVibrateNotificationKey = null;
3473 long identity = Binder.clearCallingIdentity();
3478 Binder.restoreCallingIdentity(identity);
3483 mLights.remove(canceledKey);
3485 // Record usage stats
3486 // TODO: add unbundling stats?
3488 case REASON_DELEGATE_CANCEL:
3489 case REASON_DELEGATE_CANCEL_ALL:
3490 case REASON_LISTENER_CANCEL:
3491 case REASON_LISTENER_CANCEL_ALL:
3492 mUsageStats.registerDismissedByUser(r);
3494 case REASON_APP_CANCEL:
3495 case REASON_APP_CANCEL_ALL:
3496 mUsageStats.registerRemovedByApp(r);
3500 mNotificationsByKey.remove(r.sbn.getKey());
3501 String groupKey = r.getGroupKey();
3502 NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
3503 if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
3504 mSummaryByGroupKey.remove(groupKey);
3506 final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
3507 if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
3508 summaries.remove(r.sbn.getPackageName());
3511 // Save it for users of getHistoricalNotifications()
3512 mArchive.record(r.sbn);
3514 final long now = System.currentTimeMillis();
3515 EventLogTags.writeNotificationCanceled(canceledKey, reason,
3516 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
3520 * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
3521 * and none of the {@code mustNotHaveFlags}.
3523 void cancelNotification(final int callingUid, final int callingPid,
3524 final String pkg, final String tag, final int id,
3525 final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
3526 final int userId, final int reason, final ManagedServiceInfo listener) {
3527 // In enqueueNotificationInternal notifications are added by scheduling the
3528 // work on the worker handler. Hence, we also schedule the cancel on this
3529 // handler to avoid a scenario where an add notification call followed by a
3530 // remove notification call ends up in not removing the notification.
3531 mHandler.post(new Runnable() {
3534 String listenerName = listener == null ? null : listener.component.toShortString();
3535 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
3536 userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
3538 synchronized (mNotificationList) {
3539 int index = indexOfNotificationLocked(pkg, tag, id, userId);
3541 NotificationRecord r = mNotificationList.get(index);
3543 // Ideally we'd do this in the caller of this method. However, that would
3544 // require the caller to also find the notification.
3545 if (reason == REASON_DELEGATE_CLICK) {
3546 mUsageStats.registerClickedByUser(r);
3549 if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
3552 if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
3556 mNotificationList.remove(index);
3558 cancelNotificationLocked(r, sendDelete, reason);
3559 cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
3560 REASON_GROUP_SUMMARY_CANCELED, sendDelete);
3561 updateLightsLocked();
3569 * Determine whether the userId applies to the notification in question, either because
3570 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard).
3572 private boolean notificationMatchesUserId(NotificationRecord r, int userId) {
3574 // looking for USER_ALL notifications? match everything
3575 userId == UserHandle.USER_ALL
3576 // a notification sent to USER_ALL matches any query
3577 || r.getUserId() == UserHandle.USER_ALL
3578 // an exact user match
3579 || r.getUserId() == userId;
3583 * Determine whether the userId applies to the notification in question, either because
3584 * they match exactly, or one of them is USER_ALL (which is treated as a wildcard) or
3585 * because it matches one of the users profiles.
3587 private boolean notificationMatchesCurrentProfiles(NotificationRecord r, int userId) {
3588 return notificationMatchesUserId(r, userId)
3589 || mUserProfiles.isCurrentProfile(r.getUserId());
3593 * Cancels all notifications from a given package that have all of the
3594 * {@code mustHaveFlags}.
3596 boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
3597 int mustNotHaveFlags, boolean doit, int userId, int reason,
3598 ManagedServiceInfo listener) {
3599 String listenerName = listener == null ? null : listener.component.toShortString();
3600 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3601 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
3604 synchronized (mNotificationList) {
3605 final int N = mNotificationList.size();
3606 ArrayList<NotificationRecord> canceledNotifications = null;
3607 for (int i = N-1; i >= 0; --i) {
3608 NotificationRecord r = mNotificationList.get(i);
3609 if (!notificationMatchesUserId(r, userId)) {
3612 // Don't remove notifications to all, if there's no package name specified
3613 if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
3616 if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
3619 if ((r.getFlags() & mustNotHaveFlags) != 0) {
3622 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
3625 if (canceledNotifications == null) {
3626 canceledNotifications = new ArrayList<>();
3628 canceledNotifications.add(r);
3632 mNotificationList.remove(i);
3633 cancelNotificationLocked(r, false, reason);
3635 if (doit && canceledNotifications != null) {
3636 final int M = canceledNotifications.size();
3637 for (int i = 0; i < M; i++) {
3638 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3639 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
3642 if (canceledNotifications != null) {
3643 updateLightsLocked();
3645 return canceledNotifications != null;
3649 void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
3650 ManagedServiceInfo listener, boolean includeCurrentProfiles) {
3651 String listenerName = listener == null ? null : listener.component.toShortString();
3652 EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
3653 null, userId, 0, 0, reason, listenerName);
3655 ArrayList<NotificationRecord> canceledNotifications = null;
3656 final int N = mNotificationList.size();
3657 for (int i=N-1; i>=0; i--) {
3658 NotificationRecord r = mNotificationList.get(i);
3659 if (includeCurrentProfiles) {
3660 if (!notificationMatchesCurrentProfiles(r, userId)) {
3664 if (!notificationMatchesUserId(r, userId)) {
3669 if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
3670 | Notification.FLAG_NO_CLEAR)) == 0) {
3671 mNotificationList.remove(i);
3672 cancelNotificationLocked(r, true, reason);
3673 // Make a note so we can cancel children later.
3674 if (canceledNotifications == null) {
3675 canceledNotifications = new ArrayList<>();
3677 canceledNotifications.add(r);
3680 int M = canceledNotifications != null ? canceledNotifications.size() : 0;
3681 for (int i = 0; i < M; i++) {
3682 cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
3683 listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
3685 updateLightsLocked();
3688 // Warning: The caller is responsible for invoking updateLightsLocked().
3689 private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
3690 String listenerName, int reason, boolean sendDelete) {
3691 Notification n = r.getNotification();
3692 if (!n.isGroupSummary()) {
3696 String pkg = r.sbn.getPackageName();
3697 int userId = r.getUserId();
3700 if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
3704 final int N = mNotificationList.size();
3705 for (int i = N - 1; i >= 0; i--) {
3706 NotificationRecord childR = mNotificationList.get(i);
3707 StatusBarNotification childSbn = childR.sbn;
3708 if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
3709 childR.getGroupKey().equals(r.getGroupKey())) {
3710 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
3711 childSbn.getTag(), userId, 0, 0, reason, listenerName);
3712 mNotificationList.remove(i);
3713 cancelNotificationLocked(childR, sendDelete, reason);
3718 private boolean isLedNotificationForcedOn(NotificationRecord r) {
3720 final Notification n = r.sbn.getNotification();
3721 if (n.extras != null) {
3722 return n.extras.getBoolean(Notification.EXTRA_FORCE_SHOW_LIGHTS, false);
3728 // lock on mNotificationList
3729 void updateLightsLocked()
3731 // handle notification lights
3732 NotificationRecord ledNotification = null;
3733 while (ledNotification == null && !mLights.isEmpty()) {
3734 final String owner = mLights.get(mLights.size() - 1);
3735 ledNotification = mNotificationsByKey.get(owner);
3736 if (ledNotification == null) {
3737 Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
3738 mLights.remove(owner);
3742 // Don't flash while we are in a call or screen is on
3743 // (unless Notification has EXTRA_FORCE_SHOW_LGHTS)
3744 final boolean enableLed;
3745 if (ledNotification == null) {
3747 } else if (isLedNotificationForcedOn(ledNotification)) {
3749 } else if (!mScreenOnEnabled && (mInCall || mScreenOn)) {
3756 mNotificationLight.turnOff();
3757 if (mStatusBar != null) {
3758 mStatusBar.notificationLightOff();
3761 final Notification ledno = ledNotification.sbn.getNotification();
3762 final NotificationLedValues ledValues = getLedValuesForNotification(ledNotification);
3767 if (ledValues != null) {
3768 ledARGB = ledValues.color != 0 ? ledValues.color : mDefaultNotificationColor;
3769 ledOnMS = ledValues.onMS >= 0 ? ledValues.onMS : mDefaultNotificationLedOn;
3770 ledOffMS = ledValues.offMS >= 0 ? ledValues.offMS : mDefaultNotificationLedOff;
3771 } else if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
3772 ledARGB = generateLedColorForNotification(ledNotification);
3773 ledOnMS = mDefaultNotificationLedOn;
3774 ledOffMS = mDefaultNotificationLedOff;
3776 ledARGB = ledno.ledARGB;
3777 ledOnMS = ledno.ledOnMS;
3778 ledOffMS = ledno.ledOffMS;
3781 // update the LEDs modes variables
3782 mNotificationLight.setModes(mNotificationLedBrightnessLevel,
3783 mMultipleLedsEnabledSetting);
3785 if (mNotificationPulseEnabled) {
3787 mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
3790 if (mStatusBar != null) {
3791 // let SystemUI make an independent decision
3792 mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
3797 private void parseNotificationPulseCustomValuesString(String customLedValuesString) {
3798 if (TextUtils.isEmpty(customLedValuesString)) {
3802 for (String packageValuesString : customLedValuesString.split("\\|")) {
3803 String[] packageValues = packageValuesString.split("=");
3804 if (packageValues.length != 2) {
3805 Log.e(TAG, "Error parsing custom led values for unknown package");
3808 String packageName = packageValues[0];
3809 String[] values = packageValues[1].split(";");
3810 if (values.length != 3) {
3811 Log.e(TAG, "Error parsing custom led values '"
3812 + packageValues[1] + "' for " + packageName);
3815 NotificationLedValues ledValues = new NotificationLedValues();
3817 ledValues.color = Integer.parseInt(values[0]);
3818 ledValues.onMS = Integer.parseInt(values[1]);
3819 ledValues.offMS = Integer.parseInt(values[2]);
3820 } catch (NumberFormatException e) {
3821 Log.e(TAG, "Error parsing custom led values '"
3822 + packageValues[1] + "' for " + packageName);
3825 mNotificationPulseCustomLedValues.put(packageName, ledValues);
3829 private NotificationLedValues getLedValuesForNotification(NotificationRecord ledNotification) {
3830 final String packageName = ledNotification.sbn.getPackageName();
3831 return mNotificationPulseCustomLedValues.get(mapPackage(packageName));
3834 private int generateLedColorForNotification(NotificationRecord ledNotification) {
3835 if (!mAutoGenerateNotificationColor) {
3836 return mDefaultNotificationColor;
3838 if (!mMultiColorNotificationLed) {
3839 return mDefaultNotificationColor;
3841 final String packageName = ledNotification.sbn.getPackageName();
3842 final String mapping = mapPackage(packageName);
3843 int color = mDefaultNotificationColor;
3845 if (mGeneratedPackageLedColors.containsKey(mapping)) {
3846 return mGeneratedPackageLedColors.get(mapping);
3849 PackageManager pm = getContext().getPackageManager();
3852 icon = pm.getApplicationIcon(mapping);
3853 } catch (NameNotFoundException e) {
3854 Slog.e(TAG, e.getMessage(), e);
3858 color = ColorUtils.generateAlertColorFromDrawable(icon);
3859 mGeneratedPackageLedColors.put(mapping, color);
3864 private String mapPackage(String pkg) {
3865 if (!mPackageNameMappings.containsKey(pkg)) {
3868 return mPackageNameMappings.get(pkg);
3871 // lock on mNotificationList
3872 int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
3874 ArrayList<NotificationRecord> list = mNotificationList;
3875 final int len = list.size();
3876 for (int i=0; i<len; i++) {
3877 NotificationRecord r = list.get(i);
3878 if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
3879 TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
3886 // lock on mNotificationList
3887 int indexOfNotificationLocked(String key) {
3888 final int N = mNotificationList.size();
3889 for (int i = 0; i < N; i++) {
3890 if (key.equals(mNotificationList.get(i).getKey())) {
3897 private void updateNotificationPulse() {
3898 synchronized (mNotificationList) {
3899 updateLightsLocked();
3903 private static boolean isUidSystem(int uid) {
3904 final int appid = UserHandle.getAppId(uid);
3905 return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
3908 private static boolean isCallerSystem() {
3909 return isUidSystem(Binder.getCallingUid());
3912 private static void checkCallerIsSystem() {
3913 if (isCallerSystem()) {
3916 throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
3919 private static void checkCallerIsSystemOrSameApp(String pkg) {
3920 if (isCallerSystem()) {
3923 checkCallerIsSameApp(pkg);
3926 private static void checkCallerIsSameApp(String pkg) {
3927 final int uid = Binder.getCallingUid();
3929 ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
3930 pkg, 0, UserHandle.getCallingUserId());
3932 throw new SecurityException("Unknown package " + pkg);
3934 if (!UserHandle.isSameApp(ai.uid, uid)) {
3935 throw new SecurityException("Calling uid " + uid + " gave package"
3936 + pkg + " which is owned by uid " + ai.uid);
3938 } catch (RemoteException re) {
3939 throw new SecurityException("Unknown package " + pkg + "\n" + re);
3943 private static String callStateToString(int state) {
3945 case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
3946 case TelephonyManager.CALL_STATE_RINGING: return "CALL_STATE_RINGING";
3947 case TelephonyManager.CALL_STATE_OFFHOOK: return "CALL_STATE_OFFHOOK";
3948 default: return "CALL_STATE_UNKNOWN_" + state;
3952 private void listenForCallState() {
3953 TelephonyManager.from(getContext()).listen(new PhoneStateListener() {
3955 public void onCallStateChanged(int state, String incomingNumber) {
3956 if (mCallState == state) return;
3957 if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
3960 }, PhoneStateListener.LISTEN_CALL_STATE);
3964 * Generates a NotificationRankingUpdate from 'sbns', considering only
3965 * notifications visible to the given listener.
3967 * <p>Caller must hold a lock on mNotificationList.</p>
3969 private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
3970 final int N = mNotificationList.size();
3971 ArrayList<String> keys = new ArrayList<String>(N);
3972 ArrayList<String> interceptedKeys = new ArrayList<String>(N);
3973 ArrayList<Integer> importance = new ArrayList<>(N);
3974 Bundle overrideGroupKeys = new Bundle();
3975 Bundle visibilityOverrides = new Bundle();
3976 Bundle suppressedVisualEffects = new Bundle();
3977 Bundle explanation = new Bundle();
3978 for (int i = 0; i < N; i++) {
3979 NotificationRecord record = mNotificationList.get(i);
3980 if (!isVisibleToListener(record.sbn, info)) {
3983 final String key = record.sbn.getKey();
3985 importance.add(record.getImportance());
3986 if (record.getImportanceExplanation() != null) {
3987 explanation.putCharSequence(key, record.getImportanceExplanation());
3989 if (record.isIntercepted()) {
3990 interceptedKeys.add(key);
3993 suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
3994 if (record.getPackageVisibilityOverride()
3995 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
3996 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
3998 overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
4000 final int M = keys.size();
4001 String[] keysAr = keys.toArray(new String[M]);
4002 String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
4003 int[] importanceAr = new int[M];
4004 for (int i = 0; i < M; i++) {
4005 importanceAr[i] = importance.get(i);
4007 return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
4008 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
4011 private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
4012 if (!listener.enabledAndUserMatches(sbn.getUserId())) {
4015 // TODO: remove this for older listeners.
4019 private boolean isPackageSuspendedForUser(String pkg, int uid) {
4020 int userId = UserHandle.getUserId(uid);
4022 return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
4023 } catch (RemoteException re) {
4024 throw new SecurityException("Could not talk to package manager service");
4025 } catch (IllegalArgumentException ex) {
4026 // Package not found.
4031 private class TrimCache {
4032 StatusBarNotification heavy;
4033 StatusBarNotification sbnClone;
4034 StatusBarNotification sbnCloneLight;
4036 TrimCache(StatusBarNotification sbn) {
4040 StatusBarNotification ForListener(ManagedServiceInfo info) {
4041 if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
4042 if (sbnCloneLight == null) {
4043 sbnCloneLight = heavy.cloneLight();
4045 return sbnCloneLight;
4047 if (sbnClone == null) {
4048 sbnClone = heavy.clone();
4055 public class NotificationRankers extends ManagedServices {
4057 public NotificationRankers() {
4058 super(getContext(), mHandler, mNotificationList, mUserProfiles);
4062 protected Config getConfig() {
4063 Config c = new Config();
4064 c.caption = "notification ranker service";
4065 c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
4066 c.secureSettingName = null;
4067 c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
4068 c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
4069 c.clientLabel = R.string.notification_ranker_binding_label;
4074 protected IInterface asInterface(IBinder binder) {
4075 return INotificationListener.Stub.asInterface(binder);
4079 protected boolean checkType(IInterface service) {
4080 return service instanceof INotificationListener;
4084 protected void onServiceAdded(ManagedServiceInfo info) {
4085 mListeners.registerGuestService(info);
4089 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
4090 mListeners.unregisterService(removed.service, removed.userid);
4093 public void onNotificationEnqueued(final NotificationRecord r) {
4094 final StatusBarNotification sbn = r.sbn;
4095 TrimCache trimCache = new TrimCache(sbn);
4097 // mServices is the list inside ManagedServices of all the rankers,
4098 // There should be only one, but it's a list, so while we enforce
4099 // singularity elsewhere, we keep it general here, to avoid surprises.
4100 for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
4101 boolean sbnVisible = isVisibleToListener(sbn, info);
4106 final int importance = r.getImportance();
4107 final boolean fromUser = r.isImportanceFromUser();
4108 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
4109 mHandler.post(new Runnable() {
4112 notifyEnqueued(info, sbnToPost, importance, fromUser);
4118 private void notifyEnqueued(final ManagedServiceInfo info,
4119 final StatusBarNotification sbn, int importance, boolean fromUser) {
4120 final INotificationListener ranker = (INotificationListener) info.service;
4121 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4123 ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
4124 } catch (RemoteException ex) {
4125 Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
4129 public boolean isEnabled() {
4130 return !mServices.isEmpty();
4134 public void onUserSwitched(int user) {
4135 synchronized (mNotificationList) {
4136 int i = mServices.size()-1;
4138 final ManagedServiceInfo info = mServices.get(i);
4139 unregisterService(info.service, info.userid);
4146 public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
4147 if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
4148 + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
4149 if (mRankerServicePackageName == null) {
4153 if (pkgList != null && (pkgList.length > 0)) {
4154 for (String pkgName : pkgList) {
4155 if (mRankerServicePackageName.equals(pkgName)) {
4162 protected void registerRanker() {
4163 // Find the updatable ranker and register it.
4164 if (mRankerServicePackageName == null) {
4165 Slog.w(TAG, "could not start ranker service: no package specified!");
4168 Set<ComponentName> rankerComponents = queryPackageForServices(
4169 mRankerServicePackageName, UserHandle.USER_SYSTEM);
4170 Iterator<ComponentName> iterator = rankerComponents.iterator();
4171 if (iterator.hasNext()) {
4172 ComponentName rankerComponent = iterator.next();
4173 if (iterator.hasNext()) {
4174 Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
4176 registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
4179 Slog.w(TAG, "could not start ranker service: none found");
4184 public class NotificationListeners extends ManagedServices {
4186 private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
4188 public NotificationListeners() {
4189 super(getContext(), mHandler, mNotificationList, mUserProfiles);
4193 protected Config getConfig() {
4194 Config c = new Config();
4195 c.caption = "notification listener";
4196 c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
4197 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
4198 c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
4199 c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
4200 c.clientLabel = R.string.notification_listener_binding_label;
4205 protected IInterface asInterface(IBinder binder) {
4206 return INotificationListener.Stub.asInterface(binder);
4210 protected boolean checkType(IInterface service) {
4211 return service instanceof INotificationListener;
4215 public void onServiceAdded(ManagedServiceInfo info) {
4216 final INotificationListener listener = (INotificationListener) info.service;
4217 final NotificationRankingUpdate update;
4218 synchronized (mNotificationList) {
4219 update = makeRankingUpdateLocked(info);
4222 listener.onListenerConnected(update);
4223 } catch (RemoteException e) {
4229 protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
4230 if (removeDisabledHints(removed)) {
4231 updateListenerHintsLocked();
4232 updateEffectsSuppressorLocked();
4234 mLightTrimListeners.remove(removed);
4237 public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
4238 if (trim == TRIM_LIGHT) {
4239 mLightTrimListeners.add(info);
4241 mLightTrimListeners.remove(info);
4245 public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
4246 return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
4250 * asynchronously notify all listeners about a new notification
4253 * Also takes care of removing a notification that has been visible to a listener before,
4254 * but isn't anymore.
4256 public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
4257 // Lazily initialized snapshots of the notification.
4258 TrimCache trimCache = new TrimCache(sbn);
4260 for (final ManagedServiceInfo info : mServices) {
4261 boolean sbnVisible = isVisibleToListener(sbn, info);
4262 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
4263 // This notification hasn't been and still isn't visible -> ignore.
4264 if (!oldSbnVisible && !sbnVisible) {
4267 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
4269 // This notification became invisible -> remove the old one.
4270 if (oldSbnVisible && !sbnVisible) {
4271 final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
4272 mHandler.post(new Runnable() {
4275 notifyRemoved(info, oldSbnLightClone, update);
4281 final StatusBarNotification sbnToPost = trimCache.ForListener(info);
4282 mHandler.post(new Runnable() {
4285 notifyPosted(info, sbnToPost, update);
4292 * asynchronously notify all listeners about a removed notification
4294 public void notifyRemovedLocked(StatusBarNotification sbn) {
4295 // make a copy in case changes are made to the underlying Notification object
4296 // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
4298 final StatusBarNotification sbnLight = sbn.cloneLight();
4299 for (final ManagedServiceInfo info : mServices) {
4300 if (!isVisibleToListener(sbn, info)) {
4303 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
4304 mHandler.post(new Runnable() {
4307 notifyRemoved(info, sbnLight, update);
4314 * asynchronously notify all listeners about a reordering of notifications
4316 public void notifyRankingUpdateLocked() {
4317 for (final ManagedServiceInfo serviceInfo : mServices) {
4318 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4321 final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
4322 mHandler.post(new Runnable() {
4325 notifyRankingUpdate(serviceInfo, update);
4331 public void notifyListenerHintsChangedLocked(final int hints) {
4332 for (final ManagedServiceInfo serviceInfo : mServices) {
4333 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4336 mHandler.post(new Runnable() {
4339 notifyListenerHintsChanged(serviceInfo, hints);
4345 public void notifyInterruptionFilterChanged(final int interruptionFilter) {
4346 for (final ManagedServiceInfo serviceInfo : mServices) {
4347 if (!serviceInfo.isEnabledForCurrentProfiles()) {
4350 mHandler.post(new Runnable() {
4353 notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
4359 private void notifyPosted(final ManagedServiceInfo info,
4360 final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
4361 final INotificationListener listener = (INotificationListener)info.service;
4362 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4364 listener.onNotificationPosted(sbnHolder, rankingUpdate);
4365 } catch (RemoteException ex) {
4366 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
4370 private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
4371 NotificationRankingUpdate rankingUpdate) {
4372 if (!info.enabledAndUserMatches(sbn.getUserId())) {
4375 final INotificationListener listener = (INotificationListener) info.service;
4376 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
4378 listener.onNotificationRemoved(sbnHolder, rankingUpdate);
4379 } catch (RemoteException ex) {
4380 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
4384 private void notifyRankingUpdate(ManagedServiceInfo info,
4385 NotificationRankingUpdate rankingUpdate) {
4386 final INotificationListener listener = (INotificationListener) info.service;
4388 listener.onNotificationRankingUpdate(rankingUpdate);
4389 } catch (RemoteException ex) {
4390 Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
4394 private void notifyListenerHintsChanged(ManagedServiceInfo info, int hints) {
4395 final INotificationListener listener = (INotificationListener) info.service;
4397 listener.onListenerHintsChanged(hints);
4398 } catch (RemoteException ex) {
4399 Log.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
4403 private void notifyInterruptionFilterChanged(ManagedServiceInfo info,
4404 int interruptionFilter) {
4405 final INotificationListener listener = (INotificationListener) info.service;
4407 listener.onInterruptionFilterChanged(interruptionFilter);
4408 } catch (RemoteException ex) {
4409 Log.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
4413 private boolean isListenerPackage(String packageName) {
4414 if (packageName == null) {
4417 // TODO: clean up locking object later
4418 synchronized (mNotificationList) {
4419 for (final ManagedServiceInfo serviceInfo : mServices) {
4420 if (packageName.equals(serviceInfo.component.getPackageName())) {
4429 public static final class DumpFilter {
4430 public boolean filtered = false;
4431 public String pkgFilter;
4434 public boolean stats;
4435 public boolean redact = true;
4437 public static DumpFilter parseFromArguments(String[] args) {
4438 final DumpFilter filter = new DumpFilter();
4439 for (int ai = 0; ai < args.length; ai++) {
4440 final String a = args[ai];
4441 if ("--noredact".equals(a) || "--reveal".equals(a)) {
4442 filter.redact = false;
4443 } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) {
4444 if (ai < args.length-1) {
4446 filter.pkgFilter = args[ai].trim().toLowerCase();
4447 if (filter.pkgFilter.isEmpty()) {
4448 filter.pkgFilter = null;
4450 filter.filtered = true;
4453 } else if ("--zen".equals(a) || "zen".equals(a)) {
4454 filter.filtered = true;
4456 } else if ("--stats".equals(a)) {
4457 filter.stats = true;
4458 if (ai < args.length-1) {
4460 filter.since = Long.valueOf(args[ai]);
4469 public boolean matches(StatusBarNotification sbn) {
4470 if (!filtered) return true;
4471 return zen ? true : sbn != null
4472 && (matches(sbn.getPackageName()) || matches(sbn.getOpPkg()));
4475 public boolean matches(ComponentName component) {
4476 if (!filtered) return true;
4477 return zen ? true : component != null && matches(component.getPackageName());
4480 public boolean matches(String pkg) {
4481 if (!filtered) return true;
4482 return zen ? true : pkg != null && pkg.toLowerCase().contains(pkgFilter);
4486 public String toString() {
4487 return stats ? "stats" : zen ? "zen" : ('\'' + pkgFilter + '\'');
4492 * Wrapper for a StatusBarNotification object that allows transfer across a oneway
4493 * binder without sending large amounts of data over a oneway transaction.
4495 private static final class StatusBarNotificationHolder
4496 extends IStatusBarNotificationHolder.Stub {
4497 private StatusBarNotification mValue;
4499 public StatusBarNotificationHolder(StatusBarNotification value) {
4503 /** Get the held value and clear it. This function should only be called once per holder */
4505 public StatusBarNotification get() {
4506 StatusBarNotification value = mValue;
4512 private final class PolicyAccess {
4513 private static final String SEPARATOR = ":";
4514 private final String[] PERM = {
4515 android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
4518 public boolean isPackageGranted(String pkg) {
4519 return pkg != null && getGrantedPackages().contains(pkg);
4522 public void put(String pkg, boolean granted) {
4523 if (pkg == null) return;
4524 final ArraySet<String> pkgs = getGrantedPackages();
4527 changed = pkgs.add(pkg);
4529 changed = pkgs.remove(pkg);
4531 if (!changed) return;
4532 final String setting = TextUtils.join(SEPARATOR, pkgs);
4533 final int currentUser = ActivityManager.getCurrentUser();
4534 Settings.Secure.putStringForUser(getContext().getContentResolver(),
4535 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4538 getContext().sendBroadcastAsUser(new Intent(NotificationManager
4539 .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
4541 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
4544 public ArraySet<String> getGrantedPackages() {
4545 final ArraySet<String> pkgs = new ArraySet<>();
4547 long identity = Binder.clearCallingIdentity();
4549 final String setting = Settings.Secure.getStringForUser(
4550 getContext().getContentResolver(),
4551 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
4552 ActivityManager.getCurrentUser());
4553 if (setting != null) {
4554 final String[] tokens = setting.split(SEPARATOR);
4555 for (int i = 0; i < tokens.length; i++) {
4556 String token = tokens[i];
4557 if (token != null) {
4558 token = token.trim();
4560 if (TextUtils.isEmpty(token)) {
4567 Binder.restoreCallingIdentity(identity);
4572 public String[] getRequestingPackages() throws RemoteException {
4573 final ParceledListSlice list = AppGlobals.getPackageManager()
4574 .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
4575 ActivityManager.getCurrentUser());
4576 final List<PackageInfo> pkgs = list.getList();
4577 if (pkgs == null || pkgs.isEmpty()) return new String[0];
4578 final int N = pkgs.size();
4579 final String[] rt = new String[N];
4580 for (int i = 0; i < N; i++) {
4581 rt[i] = pkgs.get(i).packageName;