Loading services/core/java/com/android/server/notification/ZenModeEventLogger.java +37 −5 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.service.notification.NotificationServiceProto.RULE_TYPE_MA import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Flags; import android.app.NotificationManager; import android.content.pm.PackageManager; Loading Loading @@ -256,13 +257,21 @@ class ZenModeEventLogger { return true; } if (Flags.modesApi() && hasActiveRuleCountDiff()) { // Rules with INTERRUPTION_FILTER_ALL were always possible but before MODES_API // they were completely useless; now they can apply effects, so we want to log // when they become active/inactive, even though DND itself (as in "notification // blocking") is off. return true; } // If zen mode didn't change, did the policy or number of active rules change? We only // care about changes that take effect while zen mode is on, so make sure the current // zen mode is not "OFF" if (mNewZenMode == ZEN_MODE_OFF) { return false; } return hasPolicyDiff() || hasRuleCountDiff(); return hasPolicyDiff() || hasActiveRuleCountDiff(); } // Does the difference in zen mode go from off to on or vice versa? Loading Loading @@ -294,6 +303,16 @@ class ZenModeEventLogger { } } if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) { // If the mode is OFF -> OFF then there cannot be any *effective* change to policy. // (Note that, in theory, a policy diff is impossible since we don't merge the // policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check). if (hasPolicyDiff() || hasChannelsBypassingDiff()) { Log.wtf(TAG, "Detected policy diff even though DND is OFF and not toggled"); } return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED; } // zen mode didn't change; we must be here because of a policy change or rule change if (hasPolicyDiff() || hasChannelsBypassingDiff()) { return ZenStateChangedEvent.DND_POLICY_CHANGED; Loading Loading @@ -345,7 +364,7 @@ class ZenModeEventLogger { * Returns whether the previous config and new config have a different number of active * automatic or manual rules. */ private boolean hasRuleCountDiff() { private boolean hasActiveRuleCountDiff() { return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig); } Loading Loading @@ -381,10 +400,12 @@ class ZenModeEventLogger { // Determine the number of (automatic & manual) rules active after the change takes place. int getNumRulesActive() { if (!Flags.modesApi()) { // If the zen mode has turned off, that means nothing can be active. if (mNewZenMode == ZEN_MODE_OFF) { return 0; } } return numActiveRulesInConfig(mNewConfig); } Loading Loading @@ -478,8 +499,19 @@ class ZenModeEventLogger { /** * Convert the new policy to a DNDPolicyProto format for output in logs. * * <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules * active, or only rules with {@code INTERRUPTION_FILTER_ALL} active) then this returns * {@code null} (which will be mapped to a missing submessage in the proto). Although this * is not the value of {@code NotificationManager#getConsolidatedNotificationPolicy()}, it * makes sense for logging since that policy is not actually influencing anything. */ @Nullable byte[] getDNDPolicyProto() { if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) { return null; } ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ProtoOutputStream proto = new ProtoOutputStream(bytes); Loading services/core/java/com/android/server/notification/ZenModeHelper.java +6 −1 Original line number Diff line number Diff line Loading @@ -1490,7 +1490,12 @@ public class ZenModeHelper { for (ZenRule automaticRule : mConfig.automaticRules.values()) { if (automaticRule.isAutomaticActive()) { // Active rules with INTERRUPTION_FILTER_ALL are not included in consolidated // policy. This is relevant in case some other active rule has a more // restrictive INTERRUPTION_FILTER but a more lenient ZenPolicy! if (!Flags.modesApi() || automaticRule.zenMode != Global.ZEN_MODE_OFF) { applyCustomPolicy(policy, automaticRule); } if (Flags.modesApi()) { deviceEffectsBuilder.add(automaticRule.zenDeviceEffects); } Loading services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java +4 −1 Original line number Diff line number Diff line Loading @@ -118,10 +118,13 @@ public class ZenModeEventLoggerFake extends ZenModeEventLogger { public DNDPolicyProto getPolicyProto(int i) throws IllegalArgumentException { checkInRange(i); byte[] policyBytes = mChanges.get(i).getDNDPolicyProto(); if (policyBytes == null) { return null; } try { return DNDPolicyProto.parseFrom(policyBytes); } catch (InvalidProtocolBufferException e) { return null; // couldn't turn it into proto throw new RuntimeException("Couldn't parse DNDPolicyProto!", e); } } Loading services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +98 −2 Original line number Diff line number Diff line Loading @@ -2429,8 +2429,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(0, mZenModeEventLogger.getNumRulesActive(1)); assertFalse(mZenModeEventLogger.getIsUserAction(1)); assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1)); if (Flags.modesApi()) { assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull(); } else { checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1)); } } @Test public void testZenModeEventLog_automaticRules(@TestParameter ModesApiFlag modesApiFlag) Loading Loading @@ -2511,7 +2515,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(0, mZenModeEventLogger.getNumRulesActive(1)); assertTrue(mZenModeEventLogger.getIsUserAction(1)); assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1)); if (Flags.modesApi()) { assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull(); } else { checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1)); } // When the system rule is enabled, this counts as an automatic action that comes from the // system and turns on DND Loading Loading @@ -3015,6 +3023,48 @@ public class ZenModeHelperTest extends UiServiceTestCase { .isEqualTo(DNDProtoEnums.CHANNEL_TYPE_NONE); } @Test @EnableFlags(Flags.FLAG_MODES_API) public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // An app adds an automatic zen rule AutomaticZenRule zenRule = new AutomaticZenRule("name", null, new ComponentName(CUSTOM_PKG_NAME, "cls"), Uri.parse("condition"), null, NotificationManager.INTERRUPTION_FILTER_ALL, true); String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); // Event 1: App activates the rule automatically. mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // Event 2: App deactivates the rule automatically. mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // In total, this represents 2 events. assertEquals(2, mZenModeEventLogger.numLoggedChanges()); // However, they are not DND_TURNED_ON/_OFF (no notification filtering is taking place). // Also, no consolidated ZenPolicy is logged (because of the same reason). assertThat(mZenModeEventLogger.getEventId(0)).isEqualTo( ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId()); assertThat(mZenModeEventLogger.getNumRulesActive(0)).isEqualTo(1); assertThat(mZenModeEventLogger.getPolicyProto(0)).isNull(); assertThat(mZenModeEventLogger.getEventId(1)).isEqualTo( ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId()); assertThat(mZenModeEventLogger.getNumRulesActive(1)).isEqualTo(0); assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull(); } @Test public void testUpdateConsolidatedPolicy_defaultRulesOnly() { setupZenConfig(); Loading Loading @@ -3202,6 +3252,52 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertFalse(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels()); } @Test @EnableFlags(Flags.FLAG_MODES_API) public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() { setupZenConfig(); // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy. // Note: rules with filter != PRIORITY should not have a custom policy. However, as of V // this is only validated on rule addition, but not on rule update. :/ // Rule 1: PRIORITY, custom policy but not very strict (in fact, less strict than default). AutomaticZenRule zenRuleWithPriority = new AutomaticZenRule("Priority", null, new ComponentName(CUSTOM_PKG_NAME, "cls"), Uri.parse("priority"), new ZenPolicy.Builder().allowMedia(true).build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String rule1Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRuleWithPriority, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); mZenModeHelper.setAutomaticZenRuleState(rule1Id, new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // Rule 2: ALL, but somehow with a super strict ZenPolicy. AutomaticZenRule zenRuleWithAll = new AutomaticZenRule("All", null, new ComponentName(CUSTOM_PKG_NAME, "cls"), Uri.parse("priority"), new ZenPolicy.Builder().disallowAllSounds().build(), NotificationManager.INTERRUPTION_FILTER_ALL, true); String rule2Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRuleWithAll, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); mZenModeHelper.setAutomaticZenRuleState(rule2Id, new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // Consolidated Policy should be default + rule1. assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isTrue(); // default } @Test public void zenRuleToAutomaticZenRule_allFields() { mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); Loading Loading
services/core/java/com/android/server/notification/ZenModeEventLogger.java +37 −5 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.service.notification.NotificationServiceProto.RULE_TYPE_MA import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Flags; import android.app.NotificationManager; import android.content.pm.PackageManager; Loading Loading @@ -256,13 +257,21 @@ class ZenModeEventLogger { return true; } if (Flags.modesApi() && hasActiveRuleCountDiff()) { // Rules with INTERRUPTION_FILTER_ALL were always possible but before MODES_API // they were completely useless; now they can apply effects, so we want to log // when they become active/inactive, even though DND itself (as in "notification // blocking") is off. return true; } // If zen mode didn't change, did the policy or number of active rules change? We only // care about changes that take effect while zen mode is on, so make sure the current // zen mode is not "OFF" if (mNewZenMode == ZEN_MODE_OFF) { return false; } return hasPolicyDiff() || hasRuleCountDiff(); return hasPolicyDiff() || hasActiveRuleCountDiff(); } // Does the difference in zen mode go from off to on or vice versa? Loading Loading @@ -294,6 +303,16 @@ class ZenModeEventLogger { } } if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) { // If the mode is OFF -> OFF then there cannot be any *effective* change to policy. // (Note that, in theory, a policy diff is impossible since we don't merge the // policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check). if (hasPolicyDiff() || hasChannelsBypassingDiff()) { Log.wtf(TAG, "Detected policy diff even though DND is OFF and not toggled"); } return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED; } // zen mode didn't change; we must be here because of a policy change or rule change if (hasPolicyDiff() || hasChannelsBypassingDiff()) { return ZenStateChangedEvent.DND_POLICY_CHANGED; Loading Loading @@ -345,7 +364,7 @@ class ZenModeEventLogger { * Returns whether the previous config and new config have a different number of active * automatic or manual rules. */ private boolean hasRuleCountDiff() { private boolean hasActiveRuleCountDiff() { return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig); } Loading Loading @@ -381,10 +400,12 @@ class ZenModeEventLogger { // Determine the number of (automatic & manual) rules active after the change takes place. int getNumRulesActive() { if (!Flags.modesApi()) { // If the zen mode has turned off, that means nothing can be active. if (mNewZenMode == ZEN_MODE_OFF) { return 0; } } return numActiveRulesInConfig(mNewConfig); } Loading Loading @@ -478,8 +499,19 @@ class ZenModeEventLogger { /** * Convert the new policy to a DNDPolicyProto format for output in logs. * * <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules * active, or only rules with {@code INTERRUPTION_FILTER_ALL} active) then this returns * {@code null} (which will be mapped to a missing submessage in the proto). Although this * is not the value of {@code NotificationManager#getConsolidatedNotificationPolicy()}, it * makes sense for logging since that policy is not actually influencing anything. */ @Nullable byte[] getDNDPolicyProto() { if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) { return null; } ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ProtoOutputStream proto = new ProtoOutputStream(bytes); Loading
services/core/java/com/android/server/notification/ZenModeHelper.java +6 −1 Original line number Diff line number Diff line Loading @@ -1490,7 +1490,12 @@ public class ZenModeHelper { for (ZenRule automaticRule : mConfig.automaticRules.values()) { if (automaticRule.isAutomaticActive()) { // Active rules with INTERRUPTION_FILTER_ALL are not included in consolidated // policy. This is relevant in case some other active rule has a more // restrictive INTERRUPTION_FILTER but a more lenient ZenPolicy! if (!Flags.modesApi() || automaticRule.zenMode != Global.ZEN_MODE_OFF) { applyCustomPolicy(policy, automaticRule); } if (Flags.modesApi()) { deviceEffectsBuilder.add(automaticRule.zenDeviceEffects); } Loading
services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java +4 −1 Original line number Diff line number Diff line Loading @@ -118,10 +118,13 @@ public class ZenModeEventLoggerFake extends ZenModeEventLogger { public DNDPolicyProto getPolicyProto(int i) throws IllegalArgumentException { checkInRange(i); byte[] policyBytes = mChanges.get(i).getDNDPolicyProto(); if (policyBytes == null) { return null; } try { return DNDPolicyProto.parseFrom(policyBytes); } catch (InvalidProtocolBufferException e) { return null; // couldn't turn it into proto throw new RuntimeException("Couldn't parse DNDPolicyProto!", e); } } Loading
services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +98 −2 Original line number Diff line number Diff line Loading @@ -2429,8 +2429,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(0, mZenModeEventLogger.getNumRulesActive(1)); assertFalse(mZenModeEventLogger.getIsUserAction(1)); assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1)); if (Flags.modesApi()) { assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull(); } else { checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1)); } } @Test public void testZenModeEventLog_automaticRules(@TestParameter ModesApiFlag modesApiFlag) Loading Loading @@ -2511,7 +2515,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(0, mZenModeEventLogger.getNumRulesActive(1)); assertTrue(mZenModeEventLogger.getIsUserAction(1)); assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1)); if (Flags.modesApi()) { assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull(); } else { checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1)); } // When the system rule is enabled, this counts as an automatic action that comes from the // system and turns on DND Loading Loading @@ -3015,6 +3023,48 @@ public class ZenModeHelperTest extends UiServiceTestCase { .isEqualTo(DNDProtoEnums.CHANNEL_TYPE_NONE); } @Test @EnableFlags(Flags.FLAG_MODES_API) public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // An app adds an automatic zen rule AutomaticZenRule zenRule = new AutomaticZenRule("name", null, new ComponentName(CUSTOM_PKG_NAME, "cls"), Uri.parse("condition"), null, NotificationManager.INTERRUPTION_FILTER_ALL, true); String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); // Event 1: App activates the rule automatically. mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE, SOURCE_SCHEDULE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // Event 2: App deactivates the rule automatically. mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_FALSE, SOURCE_SCHEDULE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // In total, this represents 2 events. assertEquals(2, mZenModeEventLogger.numLoggedChanges()); // However, they are not DND_TURNED_ON/_OFF (no notification filtering is taking place). // Also, no consolidated ZenPolicy is logged (because of the same reason). assertThat(mZenModeEventLogger.getEventId(0)).isEqualTo( ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId()); assertThat(mZenModeEventLogger.getNumRulesActive(0)).isEqualTo(1); assertThat(mZenModeEventLogger.getPolicyProto(0)).isNull(); assertThat(mZenModeEventLogger.getEventId(1)).isEqualTo( ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId()); assertThat(mZenModeEventLogger.getNumRulesActive(1)).isEqualTo(0); assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull(); } @Test public void testUpdateConsolidatedPolicy_defaultRulesOnly() { setupZenConfig(); Loading Loading @@ -3202,6 +3252,52 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertFalse(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels()); } @Test @EnableFlags(Flags.FLAG_MODES_API) public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() { setupZenConfig(); // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy. // Note: rules with filter != PRIORITY should not have a custom policy. However, as of V // this is only validated on rule addition, but not on rule update. :/ // Rule 1: PRIORITY, custom policy but not very strict (in fact, less strict than default). AutomaticZenRule zenRuleWithPriority = new AutomaticZenRule("Priority", null, new ComponentName(CUSTOM_PKG_NAME, "cls"), Uri.parse("priority"), new ZenPolicy.Builder().allowMedia(true).build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String rule1Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRuleWithPriority, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); mZenModeHelper.setAutomaticZenRuleState(rule1Id, new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // Rule 2: ALL, but somehow with a super strict ZenPolicy. AutomaticZenRule zenRuleWithAll = new AutomaticZenRule("All", null, new ComponentName(CUSTOM_PKG_NAME, "cls"), Uri.parse("priority"), new ZenPolicy.Builder().disallowAllSounds().build(), NotificationManager.INTERRUPTION_FILTER_ALL, true); String rule2Id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRuleWithAll, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); mZenModeHelper.setAutomaticZenRuleState(rule2Id, new Condition(zenRuleWithPriority.getConditionId(), "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); // Consolidated Policy should be default + rule1. assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isTrue(); // default } @Test public void zenRuleToAutomaticZenRule_allFields() { mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); Loading