From 0ae2b0b47d49784e4ef12c04fb49fef45959933f Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 14 Jan 2016 09:58:03 -0500 Subject: [PATCH 1/2] Verify zen rule owners before persisting rules. Change-Id: Ib89a17aa9d460db268b207828aa23c7802cfd7e9 --- .../server/notification/ZenModeHelper.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index f7043a601c9d5..434765bb4866f 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -27,7 +27,10 @@ import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.database.ContentObserver; @@ -45,7 +48,6 @@ import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings.Global; -import android.service.notification.IConditionListener; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenModeConfig.ScheduleInfo; @@ -91,6 +93,7 @@ public class ZenModeHelper { private final ZenModeConditions mConditions; private final SparseArray mConfigs = new SparseArray<>(); private final Metrics mMetrics = new Metrics(); + private final ConditionProviders.Config mServiceConfig; private int mZenMode; private int mUser = UserHandle.USER_SYSTEM; @@ -113,6 +116,7 @@ public class ZenModeHelper { mSettingsObserver.observe(); mFiltering = new ZenModeFiltering(mContext); mConditions = new ZenModeConditions(this, conditionProviders); + mServiceConfig = conditionProviders.getConfig(); } public Looper getLooper() { @@ -257,6 +261,9 @@ public class ZenModeHelper { } public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) { + if (!isValidOwner(automaticZenRule.getOwner())) { + throw new IllegalArgumentException("Owner is not a condition provider service"); + } ZenModeConfig newConfig; synchronized (mConfig) { if (mConfig == null) return null; @@ -361,6 +368,29 @@ public class ZenModeHelper { } } + public boolean isValidOwner(ComponentName owner) { + boolean foundOwner = false; + final PackageManager pm = mContext.getPackageManager(); + Intent queryIntent = new Intent(); + queryIntent.setComponent(owner); + + List installedServices = pm.queryIntentServicesAsUser( + queryIntent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, + UserHandle.getCallingUserId()); + if (installedServices != null) { + for (int i = 0, count = installedServices.size(); i < count; i++) { + ResolveInfo resolveInfo = installedServices.get(i); + ServiceInfo info = resolveInfo.serviceInfo; + if (mServiceConfig.bindPermission.equals(info.permission)) { + foundOwner = true; + break; + } + } + } + return foundOwner; + } + private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) { if (isNew) { rule.id = ZenModeConfig.newRuleId(); From 43b70cdc395d3a6cf3bd0a78b686a7f5d3ed86e0 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 14 Jan 2016 15:05:34 -0500 Subject: [PATCH 2/2] Allow condition providers to specify a rule instance limit. Bug: 25563007 Change-Id: I1149cb5be59668f8869c81cb46c5ead49aec933a --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + .../android/app/INotificationManager.aidl | 1 + .../java/android/app/NotificationManager.java | 12 +++ .../ConditionProviderService.java | 7 ++ .../NotificationManagerService.java | 8 ++ .../server/notification/ZenModeHelper.java | 86 ++++++++++++------- 8 files changed, 85 insertions(+), 32 deletions(-) diff --git a/api/current.txt b/api/current.txt index 358d4d5176f77..6b0d83b311aaa 100644 --- a/api/current.txt +++ b/api/current.txt @@ -33610,6 +33610,7 @@ package android.service.notification { method public abstract void onUnsubscribe(android.net.Uri); field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId"; field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity"; + field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit"; field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } diff --git a/api/system-current.txt b/api/system-current.txt index 9b66945ecbc91..b5bf237db965b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -35756,6 +35756,7 @@ package android.service.notification { method public abstract void onUnsubscribe(android.net.Uri); field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId"; field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity"; + field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit"; field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } diff --git a/api/test-current.txt b/api/test-current.txt index 41a77b3c8a92d..2a9870ee1538a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -33624,6 +33624,7 @@ package android.service.notification { method public abstract void onUnsubscribe(android.net.Uri); field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId"; field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity"; + field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit"; field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 633f6995b9863..368b8ef17cc93 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -103,6 +103,7 @@ interface INotificationManager boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule); boolean removeAutomaticZenRule(String id); boolean removeAutomaticZenRules(String packageName); + int getRuleInstanceCount(in ComponentName owner); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 9a3c820363995..faf5b11951a64 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -379,6 +379,18 @@ public class NotificationManager return null; } + /** + * @hide + */ + public int getRuleInstanceCount(ComponentName owner) { + INotificationManager service = getService(); + try { + return service.getRuleInstanceCount(owner); + } catch (RemoteException e) { + } + return 0; + } + /** * Returns AutomaticZenRules owned by the caller. * diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java index 88bd283d06a12..eff09d62e56ea 100644 --- a/core/java/android/service/notification/ConditionProviderService.java +++ b/core/java/android/service/notification/ConditionProviderService.java @@ -84,6 +84,13 @@ public abstract class ConditionProviderService extends Service { public static final String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity"; + /** + * The name of the {@code meta-data} tag containing the maximum number of rule instances that + * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances. + */ + public static final String META_DATA_RULE_INSTANCE_LIMIT = + "android.service.zen.automatic.ruleInstanceLimit"; + /** * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 018bf2d2cb274..b1fe68c82c890 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1726,6 +1726,14 @@ public class NotificationManagerService extends SystemService { return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules"); } + @Override + public int getRuleInstanceCount(ComponentName owner) throws RemoteException { + Preconditions.checkNotNull(owner, "Owner is null"); + enforceSystemOrSystemUI("getRuleInstanceCount"); + + return mZenModeHelper.getCurrentInstanceCount(owner); + } + @Override public void setInterruptionFilter(String pkg, int filter) throws RemoteException { enforcePolicyAccess(pkg, "setInterruptionFilter"); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 434765bb4866f..1d91fb7d858ae 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -48,6 +48,7 @@ import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings.Global; +import android.service.notification.ConditionProviderService; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenModeConfig.ScheduleInfo; @@ -201,7 +202,7 @@ public class ZenModeHelper { config.user = user; } synchronized (mConfig) { - setConfig(config, "onUserSwitched"); + setConfigLocked(config, "onUserSwitched"); } cleanUpZenRules(); } @@ -261,25 +262,34 @@ public class ZenModeHelper { } public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) { - if (!isValidOwner(automaticZenRule.getOwner())) { - throw new IllegalArgumentException("Owner is not a condition provider service"); + if (!TextUtils.isEmpty(automaticZenRule.getId())) { + throw new IllegalArgumentException("Rule already exists"); } + if (!isSystemRule(automaticZenRule)) { + ServiceInfo owner = getServiceInfo(automaticZenRule.getOwner()); + if (owner == null) { + throw new IllegalArgumentException("Owner is not a condition provider service"); + } + + final int ruleInstanceLimit = owner.metaData.getInt( + ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1); + if (ruleInstanceLimit > 0 && ruleInstanceLimit + < (getCurrentInstanceCount(automaticZenRule.getOwner()) + 1)) { + throw new IllegalArgumentException("Rule instance limit exceeded"); + } + } + ZenModeConfig newConfig; synchronized (mConfig) { if (mConfig == null) return null; if (DEBUG) { - Log.d(TAG, - "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" + reason); - } - if (!TextUtils.isEmpty(automaticZenRule.getId())) { - throw new IllegalArgumentException("Rule already exists"); + Log.d(TAG, "addAutomaticZenRule rule= " + automaticZenRule + " reason=" + reason); } newConfig = mConfig.copy(); - ZenRule rule = new ZenRule(); populateZenRule(automaticZenRule, rule, true); newConfig.automaticRules.put(rule.id, rule); - if (setConfig(newConfig, reason, true)) { + if (setConfigLocked(newConfig, reason, true)) { return createAutomaticZenRule(rule); } else { return null; @@ -309,7 +319,7 @@ public class ZenModeHelper { } populateZenRule(automaticZenRule, rule, false); newConfig.automaticRules.put(ruleId, rule); - return setConfig(newConfig, reason, true); + return setConfigLocked(newConfig, reason, true); } } @@ -327,7 +337,7 @@ public class ZenModeHelper { throw new SecurityException( "Cannot delete rules not owned by your condition provider"); } - return setConfig(newConfig, reason, true); + return setConfigLocked(newConfig, reason, true); } } @@ -343,10 +353,22 @@ public class ZenModeHelper { newConfig.automaticRules.removeAt(i); } } - return setConfig(newConfig, reason, true); + return setConfigLocked(newConfig, reason, true); } } + public int getCurrentInstanceCount(ComponentName owner) { + int count = 0; + synchronized (mConfig) { + for (ZenRule rule : mConfig.automaticRules.values()) { + if (rule.component != null && rule.component.equals(owner)) { + count++; + } + } + } + return count; + } + public boolean canManageAutomaticZenRule(ZenRule rule) { final int callingUid = Binder.getCallingUid(); if (callingUid == 0 || callingUid == Process.SYSTEM_UID) { @@ -368,13 +390,14 @@ public class ZenModeHelper { } } - public boolean isValidOwner(ComponentName owner) { - boolean foundOwner = false; - final PackageManager pm = mContext.getPackageManager(); + private boolean isSystemRule(AutomaticZenRule rule) { + return ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName()); + } + + private ServiceInfo getServiceInfo(ComponentName owner) { Intent queryIntent = new Intent(); queryIntent.setComponent(owner); - - List installedServices = pm.queryIntentServicesAsUser( + List installedServices = mPm.queryIntentServicesAsUser( queryIntent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, UserHandle.getCallingUserId()); @@ -383,12 +406,11 @@ public class ZenModeHelper { ResolveInfo resolveInfo = installedServices.get(i); ServiceInfo info = resolveInfo.serviceInfo; if (mServiceConfig.bindPermission.equals(info.permission)) { - foundOwner = true; - break; + return info; } } } - return foundOwner; + return null; } private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) { @@ -443,7 +465,7 @@ public class ZenModeHelper { newRule.conditionId = conditionId; newConfig.manualRule = newRule; } - setConfig(newConfig, reason, setRingerMode); + setConfigLocked(newConfig, reason, setRingerMode); } } @@ -508,7 +530,7 @@ public class ZenModeHelper { } if (DEBUG) Log.d(TAG, "readXml"); synchronized (mConfig) { - setConfig(config, "readXml"); + setConfigLocked(config, "readXml"); } } } @@ -537,7 +559,7 @@ public class ZenModeHelper { synchronized (mConfig) { final ZenModeConfig newConfig = mConfig.copy(); newConfig.applyNotificationPolicy(policy); - setConfig(newConfig, "setNotificationPolicy"); + setConfigLocked(newConfig, "setNotificationPolicy"); } } @@ -560,7 +582,7 @@ public class ZenModeHelper { } } } - setConfig(newConfig, "cleanUpZenRules"); + setConfigLocked(newConfig, "cleanUpZenRules"); } } @@ -573,30 +595,30 @@ public class ZenModeHelper { } } - public boolean setConfig(ZenModeConfig config, String reason) { - return setConfig(config, reason, true /*setRingerMode*/); + public boolean setConfigLocked(ZenModeConfig config, String reason) { + return setConfigLocked(config, reason, true /*setRingerMode*/); } public void setConfigAsync(ZenModeConfig config, String reason) { mHandler.postSetConfig(config, reason); } - private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) { + private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) { final long identity = Binder.clearCallingIdentity(); try { if (config == null || !config.isValid()) { - Log.w(TAG, "Invalid config in setConfig; " + config); + Log.w(TAG, "Invalid config in setConfigLocked; " + config); return false; } if (config.user != mUser) { // simply store away for background users mConfigs.put(config.user, config); - if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user); + if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user); return true; } mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config mConfigs.put(config.user, config); - if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable()); + if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable()); ZenLog.traceConfig(reason, mConfig, config); final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig), getNotificationPolicy(config)); @@ -1071,7 +1093,7 @@ public class ZenModeHelper { case MSG_SET_CONFIG: ConfigMessageData configData = (ConfigMessageData)msg.obj; synchronized (mConfig) { - setConfig(configData.config, configData.reason); + setConfigLocked(configData.config, configData.reason); } break; }