diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4d5238c582f7b..6f95e26ae79bc 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -34,5 +34,8 @@ interface INotificationManager void cancelToast(String pkg, ITransientNotification callback); void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived); void cancelNotificationWithTag(String pkg, String tag, int id); + + void setNotificationsEnabledForPackage(String pkg, boolean enabled); + boolean areNotificationsEnabledForPackage(String pkg); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index b7becf33d009d..da98c8075a030 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -218,11 +218,6 @@ public class PhoneStatusBar extends BaseStatusBar { private int mNavigationIconHints = 0; - // TODO(dsandler): codify this stuff in NotificationManager's header somewhere - private int mDisplayMinScore = Notification.PRIORITY_LOW * 10; - private int mIntruderMinScore = Notification.PRIORITY_HIGH * 10; - private int mIntruderInImmersiveMinScore = Notification.PRIORITY_HIGH * 10 + 5; - private class ExpandedDialog extends Dialog { ExpandedDialog(Context context) { super(context, com.android.internal.R.style.Theme_Translucent_NoTitleBar); @@ -2132,30 +2127,6 @@ public class PhoneStatusBar extends BaseStatusBar { vib.vibrate(250); } - public int getScoreThreshold() { - return mDisplayMinScore; - } - - public void setScoreThreshold(int score) { - // XXX HAX - if (mDisplayMinScore != score) { - this.mDisplayMinScore = score; - applyScoreThreshold(); - } - } - - private void applyScoreThreshold() { - int N = mNotificationData.size(); - for (int i=0; i mLights = new ArrayList(); private NotificationRecord mLedNotification; + // Notification control database. For now just contains disabled packages. + private AtomicFile mPolicyFile; + private HashSet mBlockedPackages = new HashSet(); + + private static final int DB_VERSION = 1; + + private static final String TAG_BODY = "notification-policy"; + private static final String ATTR_VERSION = "version"; + + private static final String TAG_BLOCKED_PKGS = "blocked-packages"; + private static final String TAG_PACKAGE = "package"; + private static final String ATTR_NAME = "name"; + + private void loadBlockDb() { + synchronized(mBlockedPackages) { + if (mPolicyFile == null) { + File dir = new File("/data/system"); + mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml")); + + mBlockedPackages.clear(); + + FileInputStream infile = null; + try { + infile = mPolicyFile.openRead(); + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(infile, null); + + int type; + String tag; + int version = DB_VERSION; + while ((type = parser.next()) != END_DOCUMENT) { + tag = parser.getName(); + if (type == START_TAG) { + if (TAG_BODY.equals(tag)) { + version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); + } else if (TAG_BLOCKED_PKGS.equals(tag)) { + while ((type = parser.next()) != END_DOCUMENT) { + tag = parser.getName(); + if (TAG_PACKAGE.equals(tag)) { + mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME)); + } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { + break; + } + } + } + } + } + } catch (FileNotFoundException e) { + // No data yet + } catch (IOException e) { + Log.wtf(TAG, "Unable to read blocked notifications database", e); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Unable to parse blocked notifications database", e); + } catch (XmlPullParserException e) { + Log.wtf(TAG, "Unable to parse blocked notifications database", e); + } finally { + IoUtils.closeQuietly(infile); + } + } + } + } + + 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) { + checkCallerIsSystem(); + return areNotificationsEnabledForPackageInt(pkg); + } + + // 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); + } + return enabled; + } + + public void setNotificationsEnabledForPackage(String pkg, boolean enabled) { + checkCallerIsSystem(); + if (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(); + mStatusBar = statusBar; statusBar.setNotificationCallbacks(mNotificationCallbacks); @@ -465,6 +637,13 @@ public class NotificationManagerService extends INotificationManager.Stub return ; } + 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; + } + synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); long callingId = Binder.clearCallingIdentity(); @@ -479,7 +658,7 @@ public class NotificationManagerService extends INotificationManager.Stub } else { // Limit the number of toasts that any given package except the android // package can enqueue. Prevents DOS attacks and deals with leaks. - if (!"android".equals(pkg)) { + if (!isSystemToast) { int count = 0; final int N = mToastQueue.size(); for (int i=0; i