diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b1d0305321d64..a1fdef8fe6019 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -47,6 +47,7 @@ public class AppOpsManager { public static final int OP_READ_CALENDAR = 8; public static final int OP_WRITE_CALENDAR = 9; public static final int OP_WIFI_SCAN = 10; + public static final int OP_POST_NOTIFICATION = 11; private static String[] sOpNames = new String[] { "COARSE_LOCATION", @@ -60,6 +61,7 @@ public class AppOpsManager { "READ_CALENDAR", "WRITE_CALENDAR", "WIFI_SCAN", + "POST_NOTIFICATION", }; private static String[] sOpPerms = new String[] { @@ -74,6 +76,7 @@ public class AppOpsManager { android.Manifest.permission.READ_CALENDAR, android.Manifest.permission.WRITE_CALENDAR, android.Manifest.permission.ACCESS_WIFI_STATE, + null, // no permission required for notifications }; public static String opToName(int op) { diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 62d496216748e..b4c222f8e5184 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -32,7 +32,7 @@ interface INotificationManager in Notification notification, inout int[] idReceived, int userId); void cancelNotificationWithTag(String pkg, String tag, int id, int userId); - void setNotificationsEnabledForPackage(String pkg, boolean enabled); - boolean areNotificationsEnabledForPackage(String pkg); + void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled); + boolean areNotificationsEnabledForPackage(String pkg, int uid); } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 37d7ce7baac88..adcc6ad4bfb26 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -23,6 +23,7 @@ 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.ITransientNotification; @@ -35,6 +36,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; @@ -62,21 +64,17 @@ 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.ArrayList; @@ -156,6 +154,8 @@ public class NotificationManagerService extends INotificationManager.Stub private ArrayList mLights = new ArrayList(); private NotificationRecord mLedNotification; + private final AppOpsManager mAppOps; + // Notification control database. For now just contains disabled packages. private AtomicFile mPolicyFile; private HashSet mBlockedPackages = new HashSet(); @@ -218,79 +218,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(); mHandler = new WorkerHandler(); - loadBlockDb(); + mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); + + importOldBlockDb(); mStatusBar = statusBar; statusBar.setNotificationCallbacks(mNotificationCallbacks); @@ -685,6 +643,28 @@ public class NotificationManagerService extends INotificationManager.Stub observer.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() { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); @@ -706,9 +686,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) { @@ -982,9 +964,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) {