summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/NotificationManagerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/NotificationManagerService.java')
-rw-r--r--services/java/com/android/server/NotificationManagerService.java603
1 files changed, 442 insertions, 161 deletions
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 37d7ce7..5cf1c28 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -23,8 +23,10 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.INotificationListener;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.PendingIntent;
@@ -35,6 +37,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -51,6 +54,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -62,26 +66,25 @@ import android.util.Slog;
import android.util.Xml;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.widget.RemoteViews;
import android.widget.Toast;
import com.android.internal.statusbar.StatusBarNotification;
-import com.android.internal.util.FastXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
import libcore.io.IoUtils;
@@ -120,6 +123,7 @@ public class NotificationManagerService extends INotificationManager.Stub
final Context mContext;
final IActivityManager mAm;
+ final UserManager mUserManager;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -148,6 +152,7 @@ public class NotificationManagerService extends INotificationManager.Stub
private boolean mInCall = false;
private boolean mNotificationPulseEnabled;
+ // used as a mutex for access to all active notifications & listeners
private final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
@@ -156,6 +161,11 @@ public class NotificationManagerService extends INotificationManager.Stub
private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
private NotificationRecord mLedNotification;
+ private final AppOpsManager mAppOps;
+
+ private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
+ private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>();
+
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
private HashSet<String> mBlockedPackages = new HashSet<String>();
@@ -169,6 +179,149 @@ public class NotificationManagerService extends INotificationManager.Stub
private static final String TAG_PACKAGE = "package";
private static final String ATTR_NAME = "name";
+ private class NotificationListenerInfo implements DeathRecipient {
+ INotificationListener listener;
+ String pkg;
+ int userid;
+ boolean isSystem;
+
+ public NotificationListenerInfo(INotificationListener listener, String pkg, int userid,
+ boolean isSystem) {
+ this.listener = listener;
+ this.pkg = pkg;
+ this.userid = userid;
+ this.isSystem = isSystem;
+ }
+
+ boolean enabledAndUserMatches(StatusBarNotification sbn) {
+ final int nid = sbn.getUserId();
+ if (!(isSystem || isEnabledForUser(nid))) return false;
+ if (this.userid == UserHandle.USER_ALL) return true;
+ return (nid == UserHandle.USER_ALL || nid == this.userid);
+ }
+
+ public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
+ if (!enabledAndUserMatches(sbn)) return;
+ try {
+ listener.onNotificationPosted(sbn);
+ } catch (RemoteException ex) {
+ // not there?
+ }
+ }
+
+ public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
+ if (!enabledAndUserMatches(sbn)) return;
+ try {
+ listener.onNotificationRemoved(sbn);
+ } catch (RemoteException ex) {
+ // not there?
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ unregisterListener(this.listener, this.userid);
+ }
+
+ /** convenience method for looking in mEnabledListenersForCurrentUser */
+ public boolean isEnabledForUser(int userid) {
+ for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) {
+ if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true;
+ }
+ return false;
+ }
+ }
+
+ private static class Archive {
+ static final int BUFFER_SIZE = 1000;
+ ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
+
+ public Archive() {
+
+ }
+ public void record(StatusBarNotification nr) {
+ if (mBuffer.size() == BUFFER_SIZE) {
+ mBuffer.removeFirst();
+ }
+ mBuffer.addLast(nr);
+ }
+
+ public void clear() {
+ mBuffer.clear();
+ }
+
+ public Iterator<StatusBarNotification> descendingIterator() {
+ return mBuffer.descendingIterator();
+ }
+ public Iterator<StatusBarNotification> ascendingIterator() {
+ return mBuffer.iterator();
+ }
+ public Iterator<StatusBarNotification> filter(
+ final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
+ return new Iterator<StatusBarNotification>() {
+ StatusBarNotification mNext = findNext();
+
+ private StatusBarNotification findNext() {
+ while (iter.hasNext()) {
+ StatusBarNotification nr = iter.next();
+ if ((pkg == null || nr.pkg == pkg)
+ && (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
+ return nr;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mNext == null;
+ }
+
+ @Override
+ public StatusBarNotification next() {
+ StatusBarNotification next = mNext;
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ mNext = findNext();
+ return next;
+ }
+
+ @Override
+ public void remove() {
+ iter.remove();
+ }
+ };
+ }
+
+ public StatusBarNotification[] getArray(int count) {
+ if (count == 0) count = Archive.BUFFER_SIZE;
+ final StatusBarNotification[] a
+ = new StatusBarNotification[Math.min(count, mBuffer.size())];
+ Iterator<StatusBarNotification> iter = descendingIterator();
+ int i=0;
+ while (iter.hasNext() && i < count) {
+ a[i++] = iter.next();
+ }
+ return a;
+ }
+
+ public StatusBarNotification[] getArray(int count, String pkg, int userId) {
+ if (count == 0) count = Archive.BUFFER_SIZE;
+ final StatusBarNotification[] a
+ = new StatusBarNotification[Math.min(count, mBuffer.size())];
+ Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
+ int i=0;
+ while (iter.hasNext() && i < count) {
+ a[i++] = iter.next();
+ }
+ return a;
+ }
+
+ }
+
+ Archive mArchive = new Archive();
+
private void loadBlockDb() {
synchronized(mBlockedPackages) {
if (mPolicyFile == null) {
@@ -218,79 +371,35 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
- private void writeBlockDb() {
- synchronized(mBlockedPackages) {
- FileOutputStream outfile = null;
- try {
- outfile = mPolicyFile.startWrite();
-
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outfile, "utf-8");
-
- out.startDocument(null, true);
-
- out.startTag(null, TAG_BODY); {
- out.attribute(null, ATTR_VERSION, String.valueOf(DB_VERSION));
- out.startTag(null, TAG_BLOCKED_PKGS); {
- // write all known network policies
- for (String pkg : mBlockedPackages) {
- out.startTag(null, TAG_PACKAGE); {
- out.attribute(null, ATTR_NAME, pkg);
- } out.endTag(null, TAG_PACKAGE);
- }
- } out.endTag(null, TAG_BLOCKED_PKGS);
- } out.endTag(null, TAG_BODY);
-
- out.endDocument();
-
- mPolicyFile.finishWrite(outfile);
- } catch (IOException e) {
- if (outfile != null) {
- mPolicyFile.failWrite(outfile);
- }
- }
- }
- }
-
- public boolean areNotificationsEnabledForPackage(String pkg) {
+ /**
+ * Use this when you just want to know if notifications are OK for this package.
+ */
+ public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
checkCallerIsSystem();
- return areNotificationsEnabledForPackageInt(pkg);
+ return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
+ == AppOpsManager.MODE_ALLOWED);
}
- // Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
- private boolean areNotificationsEnabledForPackageInt(String pkg) {
- final boolean enabled = !mBlockedPackages.contains(pkg);
- if (DBG) {
- Slog.v(TAG, "notifications are " + (enabled?"en":"dis") + "abled for " + pkg);
+ /** Use this when you actually want to post a notification or toast.
+ *
+ * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*().
+ */
+ private boolean noteNotificationOp(String pkg, int uid) {
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.v(TAG, "notifications are disabled by AppOps for " + pkg);
+ return false;
}
- return enabled;
+ return true;
}
- public void setNotificationsEnabledForPackage(String pkg, boolean enabled) {
+ public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
checkCallerIsSystem();
- if (DBG) {
+ if (true||DBG) {
Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
}
- if (enabled) {
- mBlockedPackages.remove(pkg);
- } else {
- mBlockedPackages.add(pkg);
-
- // Now, cancel any outstanding notifications that are part of a just-disabled app
- if (ENABLE_BLOCKED_NOTIFICATIONS) {
- synchronized (mNotificationList) {
- final int N = mNotificationList.size();
- for (int i=0; i<N; i++) {
- final NotificationRecord r = mNotificationList.get(i);
- if (r.pkg.equals(pkg)) {
- cancelNotificationLocked(r, false);
- }
- }
- }
- }
- // Don't bother canceling toasts, they'll go away soon enough.
- }
- writeBlockDb();
+ mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
+ enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
}
@@ -319,42 +428,139 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
- private static final class NotificationRecord
+ public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+ // enforce() will ensure the calling uid has the correct permission
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
+ "NotificationManagerService.getActiveNotifications");
+
+ StatusBarNotification[] tmp = null;
+ int uid = Binder.getCallingUid();
+
+ // noteOp will check to make sure the callingPkg matches the uid
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+ == AppOpsManager.MODE_ALLOWED) {
+ synchronized (mNotificationList) {
+ tmp = new StatusBarNotification[mNotificationList.size()];
+ final int N = mNotificationList.size();
+ for (int i=0; i<N; i++) {
+ tmp[i] = mNotificationList.get(i).sbn;
+ }
+ }
+ }
+ return tmp;
+ }
+
+ public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
+ // enforce() will ensure the calling uid has the correct permission
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
+ "NotificationManagerService.getHistoricalNotifications");
+
+ StatusBarNotification[] tmp = null;
+ int uid = Binder.getCallingUid();
+
+ // noteOp will check to make sure the callingPkg matches the uid
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+ == AppOpsManager.MODE_ALLOWED) {
+ synchronized (mArchive) {
+ tmp = mArchive.getArray(count);
+ }
+ }
+ return tmp;
+ }
+
+ boolean packageCanTapNotificationsForUser(final int uid, final String pkg) {
+ // Make sure the package and uid match, and that the package is allowed access
+ return (AppOpsManager.MODE_ALLOWED
+ == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg));
+ }
+
+ @Override
+ public void registerListener(final INotificationListener listener,
+ final String pkg, final int userid) {
+ // ensure system or allowed pkg
+ int uid = Binder.getCallingUid();
+ boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0);
+ if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) {
+ throw new SecurityException("Package " + pkg
+ + " may not listen for notifications");
+ }
+
+ synchronized (mNotificationList) {
+ try {
+ NotificationListenerInfo info
+ = new NotificationListenerInfo(listener, pkg, userid, isSystem);
+ listener.asBinder().linkToDeath(info, 0);
+ mListeners.add(info);
+ } catch (RemoteException e) {
+ // already dead
+ }
+ }
+ }
+
+ @Override
+ public void unregisterListener(INotificationListener listener, int userid) {
+ // no need to check permissions; if your listener binder is in the list,
+ // that's proof that you had permission to add it in the first place
+
+ synchronized (mNotificationList) {
+ final int N = mListeners.size();
+ for (int i=N-1; i>=0; i--) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (info.listener == listener && info.userid == userid) {
+ mListeners.remove(listener);
+ }
+ }
+ }
+ }
+
+ private void notifyPostedLocked(NotificationRecord n) {
+ final StatusBarNotification sbn = n.sbn;
+ for (final NotificationListenerInfo info : mListeners) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ info.notifyPostedIfUserMatch(sbn);
+ }});
+ }
+ }
+
+ private void notifyRemovedLocked(NotificationRecord n) {
+ final StatusBarNotification sbn = n.sbn;
+ for (final NotificationListenerInfo info : mListeners) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ info.notifyRemovedIfUserMatch(sbn);
+ }});
+ }
+ }
+
+ public static final class NotificationRecord
{
- final String pkg;
- final String tag;
- final int id;
- final int uid;
- final int initialPid;
- final int userId;
- final Notification notification;
- final int score;
+ final StatusBarNotification sbn;
IBinder statusBarKey;
- NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
- int userId, int score, Notification notification)
+ NotificationRecord(StatusBarNotification sbn)
{
- this.pkg = pkg;
- this.tag = tag;
- this.id = id;
- this.uid = uid;
- this.initialPid = initialPid;
- this.userId = userId;
- this.score = score;
- this.notification = notification;
+ this.sbn = sbn;
}
+ public Notification getNotification() { return sbn.notification; }
+ public int getFlags() { return sbn.notification.flags; }
+ public int getUserId() { return sbn.getUserId(); }
+
void dump(PrintWriter pw, String prefix, Context baseContext) {
+ final Notification notification = sbn.notification;
pw.println(prefix + this);
pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon)
- + " / " + idDebugString(baseContext, this.pkg, notification.icon));
+ + " / " + idDebugString(baseContext, this.sbn.pkg, notification.icon));
pw.println(prefix + " pri=" + notification.priority);
- pw.println(prefix + " score=" + this.score);
+ pw.println(prefix + " score=" + this.sbn.score);
pw.println(prefix + " contentIntent=" + notification.contentIntent);
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
pw.println(prefix + " contentView=" + notification.contentView);
- pw.println(prefix + " uid=" + uid + " userId=" + userId);
+ pw.println(prefix + " uid=" + this.sbn.uid + " userId=" + this.sbn.getUserId());
pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + " sound=" + notification.sound);
@@ -365,15 +571,12 @@ public class NotificationManagerService extends INotificationManager.Stub
}
@Override
- public final String toString()
- {
- return "NotificationRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " pkg=" + pkg
- + " id=" + Integer.toHexString(id)
- + " tag=" + tag
- + " score=" + score
- + "}";
+ public final String toString() {
+ return String.format(
+ "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)",
+ System.identityHashCode(this),
+ this.sbn.pkg, this.sbn.user, this.sbn.id, this.sbn.tag,
+ this.sbn.score, this.sbn.notification);
}
}
@@ -574,37 +777,67 @@ public class NotificationManagerService extends INotificationManager.Stub
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
+ } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+ // reload per-user settings
+ mSettingsObserver.update(null);
}
}
};
class SettingsObserver extends ContentObserver {
+ private final Uri NOTIFICATION_LIGHT_PULSE_URI
+ = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
+
+ private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
+ = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.System.getUriFor(
- Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
- update();
+ resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
+ false, this);
+ resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
+ false, this);
+ update(null);
}
- @Override public void onChange(boolean selfChange) {
- update();
+ @Override public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
}
- public void update() {
+ public void update(Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
- boolean pulseEnabled = Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
+ if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
+ boolean pulseEnabled = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateNotificationPulse();
+ }
+ }
+ if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
+ String pkglist = Settings.Secure.getString(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ mEnabledListenersForCurrentUser.clear();
+ if (pkglist != null) {
+ String[] pkgs = pkglist.split(";");
+ for (int i=0; i<pkgs.length; i++) {
+ final String pkg = pkgs[i];
+ if (pkg != null && ! "".equals(pkg)) {
+ mEnabledListenersForCurrentUser.add(pkgs[i]);
+ }
+ }
+ }
}
}
}
+ private SettingsObserver mSettingsObserver;
+
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
int[] ar = r.getIntArray(resid);
if (ar == null) {
@@ -625,10 +858,13 @@ public class NotificationManagerService extends INotificationManager.Stub
mContext = context;
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mAm = ActivityManagerNative.getDefault();
+ mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
- loadBlockDb();
+ mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+
+ importOldBlockDb();
mStatusBar = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
@@ -670,6 +906,7 @@ public class NotificationManagerService extends INotificationManager.Stub
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_USER_STOPPED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -681,8 +918,30 @@ public class NotificationManagerService extends INotificationManager.Stub
IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mIntentReceiver, sdFilter);
- SettingsObserver observer = new SettingsObserver(mHandler);
- observer.observe();
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
+ }
+
+ /**
+ * Read the old XML-based app block database and import those blockages into the AppOps system.
+ */
+ private void importOldBlockDb() {
+ loadBlockDb();
+
+ PackageManager pm = mContext.getPackageManager();
+ for (String pkg : mBlockedPackages) {
+ PackageInfo info = null;
+ try {
+ info = pm.getPackageInfo(pkg, 0);
+ setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false);
+ } catch (NameNotFoundException e) {
+ // forget you
+ }
+ }
+ mBlockedPackages.clear();
+ if (mPolicyFile != null) {
+ mPolicyFile.delete();
+ }
}
void systemReady() {
@@ -706,9 +965,11 @@ public class NotificationManagerService extends INotificationManager.Stub
final boolean isSystemToast = ("android".equals(pkg));
- if (ENABLE_BLOCKED_TOASTS && !isSystemToast && !areNotificationsEnabledForPackageInt(pkg)) {
- Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
- return;
+ if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
+ if (!isSystemToast) {
+ Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
+ return;
+ }
}
synchronized (mToastQueue) {
@@ -898,10 +1159,10 @@ public class NotificationManagerService extends INotificationManager.Stub
// Notifications
// ============================================================================
- public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
- int[] idOut, int userId)
+ public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id,
+ Notification notification, int[] idOut, int userId)
{
- enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
+ enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(),
tag, id, notification, idOut, userId);
}
@@ -911,8 +1172,8 @@ public class NotificationManagerService extends INotificationManager.Stub
// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
// uid/pid of another application)
- public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
- String tag, int id, Notification notification, int[] idOut, int userId)
+ public void enqueueNotificationInternal(String pkg, String basePkg, int callingUid,
+ int callingPid, String tag, int id, Notification notification, int[] idOut, int userId)
{
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
@@ -932,7 +1193,7 @@ public class NotificationManagerService extends INotificationManager.Stub
final int N = mNotificationList.size();
for (int i=0; i<N; i++) {
final NotificationRecord r = mNotificationList.get(i);
- if (r.pkg.equals(pkg) && r.userId == userId) {
+ if (r.sbn.pkg.equals(pkg) && r.sbn.getUserId() == userId) {
count++;
if (count >= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
@@ -982,9 +1243,11 @@ public class NotificationManagerService extends INotificationManager.Stub
// 3. Apply local rules
// blocked apps
- if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) {
- score = JUNK_SCORE;
- Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
+ if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
+ if (!isSystemNotification) {
+ score = JUNK_SCORE;
+ Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");
+ }
}
if (DBG) {
@@ -1000,10 +1263,9 @@ public class NotificationManagerService extends INotificationManager.Stub
final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
synchronized (mNotificationList) {
- NotificationRecord r = new NotificationRecord(pkg, tag, id,
- callingUid, callingPid, userId,
- score,
- notification);
+ final StatusBarNotification n = new StatusBarNotification(
+ pkg, id, tag, callingUid, callingPid, score, notification, user);
+ NotificationRecord r = new NotificationRecord(n);
NotificationRecord old = null;
int index = indexOfNotificationLocked(pkg, tag, id, userId);
@@ -1015,7 +1277,7 @@ public class NotificationManagerService extends INotificationManager.Stub
// Make sure we don't lose the foreground service state.
if (old != null) {
notification.flags |=
- old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
+ old.getNotification().flags&Notification.FLAG_FOREGROUND_SERVICE;
}
}
@@ -1035,8 +1297,6 @@ public class NotificationManagerService extends INotificationManager.Stub
}
if (notification.icon != 0) {
- final StatusBarNotification n = new StatusBarNotification(
- pkg, id, tag, r.uid, r.initialPid, score, notification, user);
if (old != null && old.statusBarKey != null) {
r.statusBarKey = old.statusBarKey;
long identity = Binder.clearCallingIdentity();
@@ -1063,6 +1323,8 @@ public class NotificationManagerService extends INotificationManager.Stub
if (currentUser == userId) {
sendAccessibilityEvent(notification, pkg);
}
+
+ notifyPostedLocked(r);
} else {
Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
if (old != null && old.statusBarKey != null) {
@@ -1073,15 +1335,18 @@ public class NotificationManagerService extends INotificationManager.Stub
finally {
Binder.restoreCallingIdentity(identity);
}
+
+ notifyRemovedLocked(r);
}
+ return; // do not play sounds, show lights, etc. for invalid notifications
}
// If we're not supposed to beep, vibrate, etc. then don't.
if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
&& (!(old != null
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
- && (r.userId == UserHandle.USER_ALL ||
- (r.userId == userId && r.userId == currentUser))
+ && (r.getUserId() == UserHandle.USER_ALL ||
+ (r.getUserId() == userId && r.getUserId() == currentUser))
&& canInterrupt
&& mSystemReady) {
@@ -1157,22 +1422,21 @@ public class NotificationManagerService extends INotificationManager.Stub
// does not have the VIBRATE permission.
long identity = Binder.clearCallingIdentity();
try {
- mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern
- : mFallbackVibrationPattern,
+ mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg,
+ useDefaultVibrate ? mDefaultVibrationPattern
+ : mFallbackVibrationPattern,
((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
} finally {
Binder.restoreCallingIdentity(identity);
}
} else if (notification.vibrate.length > 1) {
// If you want your own vibration pattern, you need the VIBRATE permission
- mVibrator.vibrate(notification.vibrate,
+ mVibrator.vibrate(r.sbn.uid, r.sbn.basePkg, notification.vibrate,
((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
}
}
}
- // this option doesn't shut off the lights
-
// light
// the most recent thing gets the light
mLights.remove(old);
@@ -1187,7 +1451,7 @@ public class NotificationManagerService extends INotificationManager.Stub
updateLightsLocked();
} else {
if (old != null
- && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
+ && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
updateLightsLocked();
}
}
@@ -1218,19 +1482,19 @@ public class NotificationManagerService extends INotificationManager.Stub
private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
// tell the app
if (sendDelete) {
- if (r.notification.deleteIntent != null) {
+ if (r.getNotification().deleteIntent != null) {
try {
- r.notification.deleteIntent.send();
+ r.getNotification().deleteIntent.send();
} catch (PendingIntent.CanceledException ex) {
// do nothing - there's no relevant way to recover, and
// no reason to let this propagate
- Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
+ Slog.w(TAG, "canceled PendingIntent for " + r.sbn.pkg, ex);
}
}
}
// status bar
- if (r.notification.icon != 0) {
+ if (r.getNotification().icon != 0) {
long identity = Binder.clearCallingIdentity();
try {
mStatusBar.removeNotification(r.statusBarKey);
@@ -1239,6 +1503,7 @@ public class NotificationManagerService extends INotificationManager.Stub
Binder.restoreCallingIdentity(identity);
}
r.statusBarKey = null;
+ notifyRemovedLocked(r);
}
// sound
@@ -1273,6 +1538,9 @@ public class NotificationManagerService extends INotificationManager.Stub
if (mLedNotification == r) {
mLedNotification = null;
}
+
+ // Save it for users of getHistoricalNotifications()
+ mArchive.record(r.sbn);
}
/**
@@ -1289,10 +1557,10 @@ public class NotificationManagerService extends INotificationManager.Stub
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
- if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
+ if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
return;
}
- if ((r.notification.flags & mustNotHaveFlags) != 0) {
+ if ((r.getNotification().flags & mustNotHaveFlags) != 0) {
return;
}
@@ -1313,9 +1581,9 @@ public class NotificationManagerService extends INotificationManager.Stub
// looking for USER_ALL notifications? match everything
userId == UserHandle.USER_ALL
// a notification sent to USER_ALL matches any query
- || r.userId == UserHandle.USER_ALL
+ || r.getUserId() == UserHandle.USER_ALL
// an exact user match
- || r.userId == userId;
+ || r.getUserId() == userId;
}
/**
@@ -1336,16 +1604,16 @@ public class NotificationManagerService extends INotificationManager.Stub
continue;
}
// Don't remove notifications to all, if there's no package name specified
- if (r.userId == UserHandle.USER_ALL && pkg == null) {
+ if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
continue;
}
- if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
+ if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
continue;
}
- if ((r.notification.flags & mustNotHaveFlags) != 0) {
+ if ((r.getFlags() & mustNotHaveFlags) != 0) {
continue;
}
- if (pkg != null && !r.pkg.equals(pkg)) {
+ if (pkg != null && !r.sbn.pkg.equals(pkg)) {
continue;
}
canceledSomething = true;
@@ -1418,7 +1686,7 @@ public class NotificationManagerService extends INotificationManager.Stub
continue;
}
- if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
+ if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
cancelNotificationLocked(r, true);
@@ -1445,10 +1713,11 @@ public class NotificationManagerService extends INotificationManager.Stub
if (mLedNotification == null || mInCall || mScreenOn) {
mNotificationLight.turnOff();
} else {
- int ledARGB = mLedNotification.notification.ledARGB;
- int ledOnMS = mLedNotification.notification.ledOnMS;
- int ledOffMS = mLedNotification.notification.ledOffMS;
- if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
+ final Notification ledno = mLedNotification.sbn.notification;
+ int ledARGB = ledno.ledARGB;
+ int ledOnMS = ledno.ledOnMS;
+ int ledOffMS = ledno.ledOffMS;
+ if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
ledARGB = mDefaultNotificationColor;
ledOnMS = mDefaultNotificationLedOn;
ledOffMS = mDefaultNotificationLedOff;
@@ -1468,19 +1737,19 @@ public class NotificationManagerService extends INotificationManager.Stub
final int len = list.size();
for (int i=0; i<len; i++) {
NotificationRecord r = list.get(i);
- if (!notificationMatchesUserId(r, userId) || r.id != id) {
+ if (!notificationMatchesUserId(r, userId) || r.sbn.id != id) {
continue;
}
if (tag == null) {
- if (r.tag != null) {
+ if (r.sbn.tag != null) {
continue;
}
} else {
- if (!tag.equals(r.tag)) {
+ if (!tag.equals(r.sbn.tag)) {
continue;
}
}
- if (r.pkg.equals(pkg)) {
+ if (r.sbn.pkg.equals(pkg)) {
return i;
}
}
@@ -1506,6 +1775,18 @@ public class NotificationManagerService extends INotificationManager.Stub
pw.println("Current Notification Manager state:");
+ pw.print(" Enabled listeners: [");
+ for (String pkg : mEnabledListenersForCurrentUser) {
+ pw.print(" " + pkg);
+ }
+ pw.println(" ]");
+
+ pw.println(" Live listeners:");
+ for (NotificationListenerInfo info : mListeners) {
+ pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener
+ + (info.isSystem?" SYSTEM":""));
+ }
+
int N;
synchronized (mToastQueue) {