diff options
5 files changed, 166 insertions, 13 deletions
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index a3afcce5fe36..863a99adacca 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -262,15 +262,12 @@ public class ZenModeConfig implements Parcelable { private static final String CONDITION_ATT_SOURCE = "source"; private static final String CONDITION_ATT_FLAGS = "flags"; - private static final String ZEN_POLICY_TAG = "zen_policy"; - private static final String MANUAL_TAG = "manual"; private static final String AUTOMATIC_TAG = "automatic"; private static final String AUTOMATIC_DELETED_TAG = "deleted"; private static final String RULE_ATT_ID = "ruleId"; private static final String RULE_ATT_ENABLED = "enabled"; - private static final String RULE_ATT_SNOOZING = "snoozing"; private static final String RULE_ATT_NAME = "name"; private static final String RULE_ATT_PKG = "pkg"; private static final String RULE_ATT_COMPONENT = "component"; @@ -286,6 +283,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ICON = "rule_icon"; private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc"; private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant"; + private static final String RULE_ATT_DISABLED_ORIGIN = "disabledOrigin"; private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale"; private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY = @@ -1170,6 +1168,10 @@ public class ZenModeConfig implements Parcelable { if (deletionInstant != null) { rt.deletionInstant = Instant.ofEpochMilli(deletionInstant); } + if (Flags.modesUi()) { + rt.disabledOrigin = safeInt(parser, RULE_ATT_DISABLED_ORIGIN, + UPDATE_ORIGIN_UNKNOWN); + } } return rt; } @@ -1224,6 +1226,9 @@ public class ZenModeConfig implements Parcelable { out.attributeLong(null, RULE_ATT_DELETION_INSTANT, rule.deletionInstant.toEpochMilli()); } + if (Flags.modesUi()) { + out.attributeInt(null, RULE_ATT_DISABLED_ORIGIN, rule.disabledOrigin); + } } } @@ -2514,6 +2519,8 @@ public class ZenModeConfig implements Parcelable { @ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields; @ZenDeviceEffects.ModifiableField public int zenDeviceEffectsUserModifiedFields; @Nullable public Instant deletionInstant; // Only set on deleted rules. + @FlaggedApi(Flags.FLAG_MODES_UI) + @ConfigChangeOrigin public int disabledOrigin = UPDATE_ORIGIN_UNKNOWN; public ZenRule() { } @@ -2552,6 +2559,9 @@ public class ZenModeConfig implements Parcelable { if (source.readInt() == 1) { deletionInstant = Instant.ofEpochMilli(source.readLong()); } + if (Flags.modesUi()) { + disabledOrigin = source.readInt(); + } } } @@ -2626,6 +2636,9 @@ public class ZenModeConfig implements Parcelable { } else { dest.writeInt(0); } + if (Flags.modesUi()) { + dest.writeInt(disabledOrigin); + } } } @@ -2671,6 +2684,9 @@ public class ZenModeConfig implements Parcelable { if (deletionInstant != null) { sb.append(",deletionInstant=").append(deletionInstant); } + if (Flags.modesUi()) { + sb.append(",disabledOrigin=").append(disabledOrigin); + } } return sb.append(']').toString(); @@ -2724,7 +2740,7 @@ public class ZenModeConfig implements Parcelable { && other.modified == modified; if (Flags.modesApi()) { - return finalEquals + finalEquals = finalEquals && Objects.equals(other.zenDeviceEffects, zenDeviceEffects) && other.allowManualInvocation == allowManualInvocation && Objects.equals(other.iconResName, iconResName) @@ -2735,6 +2751,11 @@ public class ZenModeConfig implements Parcelable { && other.zenDeviceEffectsUserModifiedFields == zenDeviceEffectsUserModifiedFields && Objects.equals(other.deletionInstant, deletionInstant); + + if (Flags.modesUi()) { + finalEquals = finalEquals + && other.disabledOrigin == disabledOrigin; + } } return finalEquals; @@ -2743,11 +2764,21 @@ public class ZenModeConfig implements Parcelable { @Override public int hashCode() { if (Flags.modesApi()) { - return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, - component, configurationActivity, pkg, id, enabler, zenPolicy, - zenDeviceEffects, modified, allowManualInvocation, iconResName, - triggerDescription, type, userModifiedFields, zenPolicyUserModifiedFields, - zenDeviceEffectsUserModifiedFields, deletionInstant); + if (Flags.modesUi()) { + return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, + component, configurationActivity, pkg, id, enabler, zenPolicy, + zenDeviceEffects, modified, allowManualInvocation, iconResName, + triggerDescription, type, userModifiedFields, + zenPolicyUserModifiedFields, + zenDeviceEffectsUserModifiedFields, deletionInstant, disabledOrigin); + } else { + return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, + component, configurationActivity, pkg, id, enabler, zenPolicy, + zenDeviceEffects, modified, allowManualInvocation, iconResName, + triggerDescription, type, userModifiedFields, + zenPolicyUserModifiedFields, + zenDeviceEffectsUserModifiedFields, deletionInstant); + } } return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, component, configurationActivity, pkg, id, enabler, zenPolicy, modified); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 2e7295eca1a2..c078409f468c 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -34,6 +34,7 @@ import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT_USER; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_RESTORE_BACKUP; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_UNKNOWN; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; @@ -1143,6 +1144,17 @@ public class ZenModeHelper { modified = true; } + if (Flags.modesUi()) { + if (!azr.isEnabled() && (isNew || rule.enabled)) { + // Creating a rule as disabled, or disabling a previously enabled rule. + // Record whodunit. + rule.disabledOrigin = origin; + } else if (azr.isEnabled()) { + // Enabling or previously enabled. Clear disabler. + rule.disabledOrigin = UPDATE_ORIGIN_UNKNOWN; + } + } + if (!Objects.equals(rule.conditionId, azr.getConditionId())) { rule.conditionId = azr.getConditionId(); modified = true; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 9352c1287ee1..f5ab95c1ab4c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -511,6 +511,9 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.iconResName = ICON_RES_NAME; rule.triggerDescription = TRIGGER_DESC; rule.deletionInstant = Instant.ofEpochMilli(1701790147000L); + if (Flags.modesUi()) { + rule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_USER; + } Parcel parcel = Parcel.obtain(); rule.writeToParcel(parcel, 0); @@ -540,6 +543,9 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.triggerDescription, parceled.triggerDescription); assertEquals(rule.zenPolicy, parceled.zenPolicy); assertEquals(rule.deletionInstant, parceled.deletionInstant); + if (Flags.modesUi()) { + assertEquals(rule.disabledOrigin, parceled.disabledOrigin); + } assertEquals(rule, parceled); assertEquals(rule.hashCode(), parceled.hashCode()); @@ -620,6 +626,9 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.iconResName = ICON_RES_NAME; rule.triggerDescription = TRIGGER_DESC; rule.deletionInstant = Instant.ofEpochMilli(1701790147000L); + if (Flags.modesUi()) { + rule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_APP; + } ByteArrayOutputStream baos = new ByteArrayOutputStream(); writeRuleXml(rule, baos); @@ -653,6 +662,9 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.triggerDescription, fromXml.triggerDescription); assertEquals(rule.iconResName, fromXml.iconResName); assertEquals(rule.deletionInstant, fromXml.deletionInstant); + if (Flags.modesUi()) { + assertEquals(rule.disabledOrigin, fromXml.disabledOrigin); + } } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index 26a13cb47563..57587f7dc38a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.app.Flags.FLAG_MODES_API; import static android.app.Flags.FLAG_MODES_UI; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +33,7 @@ import android.app.Flags; import android.app.NotificationManager; import android.content.ComponentName; import android.net.Uri; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -79,16 +81,17 @@ public class ZenModeDiffTest extends UiServiceTestCase { : Set.of("version", "manualRule", "automaticRules"); // Differences for flagged fields are only generated if the flag is enabled. - // "Metadata" fields (userModifiedFields & co, deletionInstant) are not compared. + // "Metadata" fields (userModifiedFields, deletionInstant, disabledOrigin) are not compared. private static final Set<String> ZEN_RULE_EXEMPT_FIELDS = android.app.Flags.modesApi() ? Set.of("userModifiedFields", "zenPolicyUserModifiedFields", - "zenDeviceEffectsUserModifiedFields", "deletionInstant") + "zenDeviceEffectsUserModifiedFields", "deletionInstant", + "disabledOrigin") : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION, RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL, RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields", "zenPolicyUserModifiedFields", "zenDeviceEffectsUserModifiedFields", - "deletionInstant"); + "deletionInstant", "disabledOrigin"); // allowPriorityChannels is flagged by android.app.modes_api public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS = @@ -201,8 +204,8 @@ public class ZenModeDiffTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testConfigDiff_fieldDiffs_flagOn() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); // these two start the same ZenModeConfig c1 = new ZenModeConfig(); ZenModeConfig c2 = new ZenModeConfig(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 40b0d78f0640..7bb633e6e1e0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -6226,6 +6226,101 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects); } + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void addAutomaticZenRule_startsDisabled_recordsDisabledOrigin() { + AutomaticZenRule startsDisabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY) + .setOwner(new ComponentName(mPkg, "SomeProvider")) + .setEnabled(false) + .build(); + + String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsDisabled, UPDATE_ORIGIN_APP, + "new", CUSTOM_PKG_UID); + + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_APP); + } + + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void updateAutomaticZenRule_disabling_recordsDisabledOrigin() { + AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY) + .setOwner(new ComponentName(mPkg, "SomeProvider")) + .setEnabled(true) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled, UPDATE_ORIGIN_APP, + "new", CUSTOM_PKG_UID); + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_UNKNOWN); + + AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled) + .setEnabled(false) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, UPDATE_ORIGIN_USER, "off", + Process.SYSTEM_UID); + + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_USER); + } + + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void updateAutomaticZenRule_keepingDisabled_preservesPreviousDisabledOrigin() { + AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY) + .setOwner(new ComponentName(mPkg, "SomeProvider")) + .setEnabled(true) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled, UPDATE_ORIGIN_APP, + "new", CUSTOM_PKG_UID); + AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled) + .setEnabled(false) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, UPDATE_ORIGIN_USER, "off", + Process.SYSTEM_UID); + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_USER); + + // Now update it again, for an unrelated reason with a different origin. + AutomaticZenRule nowRenamed = new AutomaticZenRule.Builder(nowDisabled) + .setName("Fancy pants rule") + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, nowRenamed, UPDATE_ORIGIN_APP, "update", + CUSTOM_PKG_UID); + + // Identity of the disabler is preserved. + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_USER); + } + + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void updateAutomaticZenRule_enabling_clearsDisabledOrigin() { + AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY) + .setOwner(new ComponentName(mPkg, "SomeProvider")) + .setEnabled(true) + .build(); + String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled, UPDATE_ORIGIN_APP, + "new", CUSTOM_PKG_UID); + AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled) + .setEnabled(false) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, UPDATE_ORIGIN_USER, "off", + Process.SYSTEM_UID); + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_USER); + + // Now enable it again + AutomaticZenRule nowEnabled = new AutomaticZenRule.Builder(nowDisabled) + .setEnabled(true) + .build(); + mZenModeHelper.updateAutomaticZenRule(ruleId, nowEnabled, UPDATE_ORIGIN_APP, "on", + CUSTOM_PKG_UID); + + // Identity of the disabler was cleared. + assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo( + UPDATE_ORIGIN_UNKNOWN); + } + private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode, @Nullable ZenPolicy zenPolicy) { ZenRule rule = new ZenRule(); |