Merge "Include allowChannels info in DNDPolicyProto output." into main
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 3a4a0c5..b1680ab 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -1241,7 +1241,7 @@
      * @hide
      */
     public byte[] toProto() {
-        // TODO: b/308672510 - log new ZenPolicy fields to DNDPolicyProto.
+        // TODO: b/308672510 - log user-customized ZenPolicy fields to DNDPolicyProto.
         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
         ProtoOutputStream proto = new ProtoOutputStream(bytes);
 
@@ -1267,6 +1267,10 @@
         proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders());
         proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders());
 
+        if (Flags.modesApi()) {
+            proto.write(DNDPolicyProto.ALLOW_CHANNELS, getAllowedChannels());
+        }
+
         proto.flush();
         return bytes.toByteArray();
     }
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 17ca7c8..a2978be 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -359,6 +359,8 @@
     optional PeopleType allow_messages_from = 18;
 
     optional ConversationType allow_conversations_from = 19;
+
+    optional ChannelType allow_channels = 20;
 }
 
 // Enum identifying the type of rule that changed; values set to match ones used in the
@@ -368,3 +370,11 @@
     RULE_TYPE_MANUAL = 1;
     RULE_TYPE_AUTOMATIC = 2;
 }
+
+// Enum used in DNDPolicyProto to indicate the type of channels permitted to
+// break through DND. Mirrors values in ZenPolicy.
+enum ChannelType {
+    CHANNEL_TYPE_UNSET = 0;
+    CHANNEL_TYPE_PRIORITY = 1;
+    CHANNEL_TYPE_NONE = 2;
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index 1641d4a..87158cd 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -23,6 +23,7 @@
 import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
 
 import android.annotation.NonNull;
+import android.app.Flags;
 import android.app.NotificationManager;
 import android.content.pm.PackageManager;
 import android.os.Process;
@@ -502,6 +503,13 @@
                         ZenModeConfig.getZenPolicySenders(mNewPolicy.allowMessagesFrom()));
                 proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
                         mNewPolicy.allowConversationsFrom());
+
+                if (Flags.modesApi()) {
+                    proto.write(DNDPolicyProto.ALLOW_CHANNELS,
+                            mNewPolicy.allowPriorityChannels()
+                                    ? ZenPolicy.CHANNEL_TYPE_PRIORITY
+                                    : ZenPolicy.CHANNEL_TYPE_NONE);
+                }
             } else {
                 Log.wtf(TAG, "attempted to write zen mode log event with null policy");
             }
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 a44986a..4d25eaa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1052,6 +1052,88 @@
     }
 
     @Test
+    public void testProtoWithAutoRuleCustomPolicy_classic() throws Exception {
+        setupZenConfig();
+        // clear any automatic rules just to make sure
+        mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
+
+        // Add an automatic rule with a custom policy
+        ZenRule rule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, CUSTOM_RULE_ID);
+        rule.zenPolicy = new ZenPolicy.Builder()
+                .allowAlarms(true)
+                .allowRepeatCallers(false)
+                .allowCalls(PEOPLE_TYPE_STARRED)
+                .build();
+        mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelper.pullRules(events);
+
+        boolean foundCustomEvent = false;
+        for (StatsEvent ev : events) {
+            AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+            assertTrue(atom.hasDndModeRule());
+            DNDModeProto cfg = atom.getDndModeRule();
+            if (cfg.getUid() == CUSTOM_PKG_UID) {
+                foundCustomEvent = true;
+                // Check that the pieces of the policy are applied.
+                assertThat(cfg.hasPolicy()).isTrue();
+                DNDPolicyProto policy = cfg.getPolicy();
+                assertThat(policy.getAlarms().getNumber()).isEqualTo(DNDProtoEnums.STATE_ALLOW);
+                assertThat(policy.getRepeatCallers().getNumber())
+                        .isEqualTo(DNDProtoEnums.STATE_DISALLOW);
+                assertThat(policy.getCalls().getNumber()).isEqualTo(DNDProtoEnums.STATE_ALLOW);
+                assertThat(policy.getAllowCallsFrom().getNumber())
+                        .isEqualTo(DNDProtoEnums.PEOPLE_STARRED);
+            }
+        }
+        assertTrue("couldn't find custom rule", foundCustomEvent);
+    }
+
+    @Test
+    public void testProtoWithAutoRuleCustomPolicy() throws Exception {
+        // allowChannels is only valid under modes_api.
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        setupZenConfig();
+        // clear any automatic rules just to make sure
+        mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
+
+        // Add an automatic rule with a custom policy
+        ZenRule rule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, CUSTOM_RULE_ID);
+        rule.zenPolicy = new ZenPolicy.Builder()
+                .allowAlarms(true)
+                .allowRepeatCallers(false)
+                .allowCalls(PEOPLE_TYPE_STARRED)
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_NONE)
+                .build();
+        mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelper.pullRules(events);
+
+        boolean foundCustomEvent = false;
+        for (StatsEvent ev : events) {
+            AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+            assertTrue(atom.hasDndModeRule());
+            DNDModeProto cfg = atom.getDndModeRule();
+            if (cfg.getUid() == CUSTOM_PKG_UID) {
+                foundCustomEvent = true;
+                // Check that the pieces of the policy are applied.
+                assertThat(cfg.hasPolicy()).isTrue();
+                DNDPolicyProto policy = cfg.getPolicy();
+                assertThat(policy.getAlarms().getNumber()).isEqualTo(DNDProtoEnums.STATE_ALLOW);
+                assertThat(policy.getRepeatCallers().getNumber())
+                        .isEqualTo(DNDProtoEnums.STATE_DISALLOW);
+                assertThat(policy.getCalls().getNumber()).isEqualTo(DNDProtoEnums.STATE_ALLOW);
+                assertThat(policy.getAllowCallsFrom().getNumber())
+                        .isEqualTo(DNDProtoEnums.PEOPLE_STARRED);
+                assertThat(policy.getAllowChannels().getNumber())
+                        .isEqualTo(DNDProtoEnums.CHANNEL_TYPE_NONE);
+            }
+        }
+        assertTrue("couldn't find custom rule", foundCustomEvent);
+    }
+
+    @Test
     public void ruleUidsCached() throws Exception {
         setupZenConfig();
         // one enabled automatic rule
@@ -2722,6 +2804,55 @@
     }
 
     @Test
+    public void testZenModeEventLog_policyAllowChannels() {
+        // when modes_api flag is on, ensure that any change in allow_channels gets logged,
+        // even when there are no other changes.
+        mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        // Default zen config has allow channels = priority (aka on)
+        setupZenConfig();
+
+        // First just turn zen mode on
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
+                Process.SYSTEM_UID, true);
+
+        // Now change only the channels part of the policy; want to confirm that this'll be
+        // reflected in the logs
+        ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
+        newConfig.allowPriorityChannels = false;
+        mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
+                true);
+
+        // Total events: one for turning on, one for changing policy
+        assertThat(mZenModeEventLogger.numLoggedChanges()).isEqualTo(2);
+
+        // The first event is just turning DND on; make sure the policy is what we expect there
+        // before it changes in the next stage
+        assertThat(mZenModeEventLogger.getEventId(0))
+                .isEqualTo(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId());
+        DNDPolicyProto origDndProto = mZenModeEventLogger.getPolicyProto(0);
+        checkDndProtoMatchesSetupZenConfig(origDndProto);
+        assertThat(origDndProto.getAllowChannels().getNumber())
+                .isEqualTo(DNDProtoEnums.CHANNEL_TYPE_PRIORITY);
+
+        // Second message where we change the policy:
+        //   - DND_POLICY_CHANGED (indicates only the policy changed and nothing else)
+        //   - rule type: unknown (it's a policy change, not a rule change)
+        //   - user action (because it comes from a "system" uid)
+        //   - change is in allow channels, and final policy
+        assertThat(mZenModeEventLogger.getEventId(1))
+                .isEqualTo(ZenModeEventLogger.ZenStateChangedEvent.DND_POLICY_CHANGED.getId());
+        assertThat(mZenModeEventLogger.getChangedRuleType(1))
+                .isEqualTo(DNDProtoEnums.UNKNOWN_RULE);
+        assertThat(mZenModeEventLogger.getIsUserAction(1)).isTrue();
+        assertThat(mZenModeEventLogger.getPackageUid(1)).isEqualTo(Process.SYSTEM_UID);
+        DNDPolicyProto dndProto = mZenModeEventLogger.getPolicyProto(1);
+        assertThat(dndProto.getAllowChannels().getNumber())
+                .isEqualTo(DNDProtoEnums.CHANNEL_TYPE_NONE);
+    }
+
+    @Test
     public void testUpdateConsolidatedPolicy_defaultRulesOnly() {
         setupZenConfig();
 
@@ -3416,6 +3547,7 @@
         return rule;
     }
 
+    // TODO: b/310620812 - Update setup methods to include allowChannels() when MODES_API is inlined
     private void setupZenConfig() {
         mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.mConfig.allowAlarms = false;