diff options
| author | 2013-03-21 22:01:59 +0000 | |
|---|---|---|
| committer | 2013-03-21 22:01:59 +0000 | |
| commit | dc0299fb2373faddd547d433ffa049fb40d64fac (patch) | |
| tree | e48b25eef99c35425d3ab642741ea131acee39ae | |
| parent | c3ab03a9f2c0bc2c4ce8ade6957bcfc30fe2f9ee (diff) | |
| parent | 4b749ef5f2d510b84b99aa269d5c77af22457d04 (diff) | |
Merge "Allow whitelisted non-system packages to listen for notifications." into jb-mr2-dev
| -rw-r--r-- | core/java/android/app/INotificationManager.aidl | 2 | ||||
| -rw-r--r-- | core/java/android/provider/Settings.java | 8 | ||||
| -rw-r--r-- | services/java/com/android/server/NotificationManagerService.java | 129 |
3 files changed, 114 insertions, 25 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 14bcc0d7fba4..3d9b2ae83518 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -41,7 +41,7 @@ interface INotificationManager StatusBarNotification[] getActiveNotifications(String callingPkg); StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count); - void registerListener(in INotificationListener listener, int userid); + void registerListener(in INotificationListener listener, String pkg, int userid); void unregisterListener(in INotificationListener listener, int userid); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8346cc169919..19283be3897f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4039,6 +4039,14 @@ public final class Settings { public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component"; /** + * Name of a package that the current user has explicitly allowed to see all of that + * user's notifications. + * + * @hide + */ + public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 9e036d1124ec..5cf1c285f2db 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -50,12 +50,11 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.os.Parcel; -import android.os.Parcelable; 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; @@ -124,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; @@ -164,6 +164,7 @@ public class NotificationManagerService extends INotificationManager.Stub 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; @@ -180,20 +181,27 @@ public class NotificationManagerService extends INotificationManager.Stub private class NotificationListenerInfo implements DeathRecipient { INotificationListener listener; + String pkg; int userid; - public NotificationListenerInfo(INotificationListener listener, 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 userMatches(StatusBarNotification sbn) { + boolean enabledAndUserMatches(StatusBarNotification sbn) { + final int nid = sbn.getUserId(); + if (!(isSystem || isEnabledForUser(nid))) return false; if (this.userid == UserHandle.USER_ALL) return true; - int nid = sbn.getUserId(); return (nid == UserHandle.USER_ALL || nid == this.userid); } public void notifyPostedIfUserMatch(StatusBarNotification sbn) { - if (!userMatches(sbn)) return; + if (!enabledAndUserMatches(sbn)) return; try { listener.onNotificationPosted(sbn); } catch (RemoteException ex) { @@ -202,7 +210,7 @@ public class NotificationManagerService extends INotificationManager.Stub } public void notifyRemovedIfUserMatch(StatusBarNotification sbn) { - if (!userMatches(sbn)) return; + if (!enabledAndUserMatches(sbn)) return; try { listener.onNotificationRemoved(sbn); } catch (RemoteException ex) { @@ -214,6 +222,14 @@ public class NotificationManagerService extends INotificationManager.Stub 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 { @@ -413,12 +429,14 @@ public class NotificationManagerService extends INotificationManager.Stub } 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) { @@ -433,12 +451,14 @@ public class NotificationManagerService extends INotificationManager.Stub } 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) { @@ -448,12 +468,27 @@ public class NotificationManagerService extends INotificationManager.Stub 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 int userid) { - checkCallerIsSystem(); + 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, userid); + NotificationListenerInfo info + = new NotificationListenerInfo(listener, pkg, userid, isSystem); listener.asBinder().linkToDeath(info, 0); mListeners.add(info); } catch (RemoteException e) { @@ -464,7 +499,9 @@ public class NotificationManagerService extends INotificationManager.Stub @Override public void unregisterListener(INotificationListener listener, int userid) { - checkCallerIsSystem(); + // 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--) { @@ -740,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) { @@ -791,6 +858,7 @@ 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(); @@ -838,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); @@ -849,8 +918,8 @@ 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(); } /** @@ -1706,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) { |