summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Daniel Sandler <dsandler@android.com> 2013-03-21 22:01:59 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2013-03-21 22:01:59 +0000
commitdc0299fb2373faddd547d433ffa049fb40d64fac (patch)
treee48b25eef99c35425d3ab642741ea131acee39ae
parentc3ab03a9f2c0bc2c4ce8ade6957bcfc30fe2f9ee (diff)
parent4b749ef5f2d510b84b99aa269d5c77af22457d04 (diff)
Merge "Allow whitelisted non-system packages to listen for notifications." into jb-mr2-dev
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--services/java/com/android/server/NotificationManagerService.java129
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) {