diff options
Diffstat (limited to 'services/java/com/android/server/NotificationManagerService.java')
-rw-r--r-- | services/java/com/android/server/NotificationManagerService.java | 603 |
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) { |