Merge "Keep legacy suppressed-effect Policy fields in MODES_UI" into main
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e16a6a1..7ca248d 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -211,6 +211,9 @@
             SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
                     | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT;
 
+    private static final int LEGACY_SUPPRESSED_EFFECTS =
+            Policy.SUPPRESSED_EFFECT_SCREEN_ON | Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+
     // ZenModeConfig XML versions distinguishing key changes.
     public static final int XML_VERSION_ZEN_UPGRADE = 8;
     public static final int XML_VERSION_MODES_API = 11;
@@ -284,6 +287,7 @@
     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 RULE_ATT_LEGACY_SUPPRESSED_EFFECTS = "legacySuppressedEffects";
 
     private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
     private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
@@ -1171,6 +1175,8 @@
             if (Flags.modesUi()) {
                 rt.disabledOrigin = safeInt(parser, RULE_ATT_DISABLED_ORIGIN,
                         UPDATE_ORIGIN_UNKNOWN);
+                rt.legacySuppressedEffects = safeInt(parser,
+                        RULE_ATT_LEGACY_SUPPRESSED_EFFECTS, 0);
             }
         }
         return rt;
@@ -1228,6 +1234,8 @@
             }
             if (Flags.modesUi()) {
                 out.attributeInt(null, RULE_ATT_DISABLED_ORIGIN, rule.disabledOrigin);
+                out.attributeInt(null, RULE_ATT_LEGACY_SUPPRESSED_EFFECTS,
+                        rule.legacySuppressedEffects);
             }
         }
     }
@@ -1903,6 +1911,13 @@
                             ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) {
                 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
             }
+
+            // Restore legacy suppressed effects (obsolete fields which are not in ZenPolicy).
+            // These are deprecated and have no effect on behavior, however apps should get them
+            // back if provided to setNotificationPolicy() earlier.
+            suppressedVisualEffects &= ~LEGACY_SUPPRESSED_EFFECTS;
+            suppressedVisualEffects |=
+                    (LEGACY_SUPPRESSED_EFFECTS & manualRule.legacySuppressedEffects);
         } else {
             if (isAllowConversations()) {
                 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
@@ -1996,6 +2011,8 @@
         if (policy == null) return;
         if (Flags.modesUi()) {
             manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+            manualRule.legacySuppressedEffects =
+                    LEGACY_SUPPRESSED_EFFECTS & policy.suppressedVisualEffects;
         } else {
             setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0);
             allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
@@ -2521,6 +2538,10 @@
         @Nullable public Instant deletionInstant; // Only set on deleted rules.
         @FlaggedApi(Flags.FLAG_MODES_UI)
         @ConfigChangeOrigin public int disabledOrigin = UPDATE_ORIGIN_UNKNOWN;
+        // The obsolete suppressed effects in NM.Policy (SCREEN_ON, SCREEN_OFF) cannot be put in a
+        // ZenPolicy, so we store them here, only for the manual rule.
+        @FlaggedApi(Flags.FLAG_MODES_UI)
+        int legacySuppressedEffects;
 
         public ZenRule() { }
 
@@ -2561,6 +2582,7 @@
                 }
                 if (Flags.modesUi()) {
                     disabledOrigin = source.readInt();
+                    legacySuppressedEffects = source.readInt();
                 }
             }
         }
@@ -2638,6 +2660,7 @@
                 }
                 if (Flags.modesUi()) {
                     dest.writeInt(disabledOrigin);
+                    dest.writeInt(legacySuppressedEffects);
                 }
             }
         }
@@ -2686,6 +2709,7 @@
                 }
                 if (Flags.modesUi()) {
                     sb.append(",disabledOrigin=").append(disabledOrigin);
+                    sb.append(",legacySuppressedEffects=").append(legacySuppressedEffects);
                 }
             }
 
@@ -2754,7 +2778,8 @@
 
                 if (Flags.modesUi()) {
                     finalEquals = finalEquals
-                            && other.disabledOrigin == disabledOrigin;
+                            && other.disabledOrigin == disabledOrigin
+                            && other.legacySuppressedEffects == legacySuppressedEffects;
                 }
             }
 
@@ -2769,15 +2794,15 @@
                             component, configurationActivity, pkg, id, enabler, zenPolicy,
                             zenDeviceEffects, modified, allowManualInvocation, iconResName,
                             triggerDescription, type, userModifiedFields,
-                            zenPolicyUserModifiedFields,
-                            zenDeviceEffectsUserModifiedFields, deletionInstant, disabledOrigin);
+                            zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
+                            deletionInstant, disabledOrigin, legacySuppressedEffects);
                 } 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);
+                            zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
+                            deletionInstant);
                 }
             }
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index 91ef11c..a37e227 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -472,6 +472,7 @@
         public static final String FIELD_ICON_RES = "iconResName";
         public static final String FIELD_TRIGGER_DESCRIPTION = "triggerDescription";
         public static final String FIELD_TYPE = "type";
+        public static final String FIELD_LEGACY_SUPPRESSED_EFFECTS = "legacySuppressedEffects";
         // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule
 
         // Special field to track whether this rule became active or inactive
@@ -567,6 +568,13 @@
                 if (!Objects.equals(from.iconResName, to.iconResName)) {
                     addField(FIELD_ICON_RES, new FieldDiff<>(from.iconResName, to.iconResName));
                 }
+                if (android.app.Flags.modesUi()) {
+                    if (from.legacySuppressedEffects != to.legacySuppressedEffects) {
+                        addField(FIELD_LEGACY_SUPPRESSED_EFFECTS,
+                                new FieldDiff<>(from.legacySuppressedEffects,
+                                        to.legacySuppressedEffects));
+                    }
+                }
             }
         }
 
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 f7340ab..3c3c2f3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -19,12 +19,18 @@
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.Flags.modesUi;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.suppressedEffectsToString;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
 import static android.service.notification.Condition.SOURCE_UNKNOWN;
 import static android.service.notification.Condition.SOURCE_USER_ACTION;
 import static android.service.notification.Condition.STATE_FALSE;
 import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
@@ -219,8 +225,8 @@
         priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
 
         Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
                 priorityMessageSenders, suppressedVisualEffects, 0, priorityConversationsSenders);
@@ -256,8 +262,8 @@
         priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
-        suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+        suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
 
         Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
                 priorityMessageSenders, suppressedVisualEffects,
@@ -309,8 +315,8 @@
             config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED);
             config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
             config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
-                    | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
-                    | Policy.SUPPRESSED_EFFECT_AMBIENT);
+                    | Policy.SUPPRESSED_EFFECT_BADGE | SUPPRESSED_EFFECT_LIGHTS
+                    | SUPPRESSED_EFFECT_AMBIENT);
         }
         ZenPolicy actual = config.getZenPolicy();
 
@@ -357,8 +363,8 @@
             config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
             config.setAllowPriorityChannels(false);
             config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
-                    | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
-                    | Policy.SUPPRESSED_EFFECT_AMBIENT);
+                    | Policy.SUPPRESSED_EFFECT_BADGE | SUPPRESSED_EFFECT_LIGHTS
+                    | SUPPRESSED_EFFECT_AMBIENT);
         }
         ZenPolicy actual = config.getZenPolicy();
 
@@ -1063,6 +1069,43 @@
                 .isEqualTo("name");
     }
 
+    @Test
+    public void toNotificationPolicy_withNewSuppressedEffects_returnsSuppressedEffects() {
+        ZenModeConfig config = getCustomConfig();
+        // From LegacyNotificationManagerTest.testSetNotificationPolicy_preP_setNewFields
+        // When a pre-P app sets SUPPRESSED_EFFECT_NOTIFICATION_LIST, it's converted by NMS into:
+        Policy policy = new Policy(0, 0, 0,
+                SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS
+                        | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT);
+
+        config.applyNotificationPolicy(policy);
+        Policy result = config.toNotificationPolicy();
+
+        assertThat(suppressedEffectsOf(result)).isEqualTo(suppressedEffectsOf(policy));
+    }
+
+    @Test
+    public void toNotificationPolicy_withOldAndNewSuppressedEffects_returnsSuppressedEffects() {
+        ZenModeConfig config = getCustomConfig();
+        // From LegacyNotificationManagerTest.testSetNotificationPolicy_preP_setOldNewFields.
+        // When a pre-P app sets SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR, it's
+        // converted by NMS into:
+        Policy policy = new Policy(0, 0, 0,
+                SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+                        | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK
+                        | SUPPRESSED_EFFECT_AMBIENT);
+
+        config.applyNotificationPolicy(policy);
+        Policy result = config.toNotificationPolicy();
+
+        assertThat(suppressedEffectsOf(result)).isEqualTo(suppressedEffectsOf(policy));
+    }
+
+    private static String suppressedEffectsOf(Policy policy) {
+        return suppressedEffectsToString(policy.suppressedVisualEffects) + "("
+                + policy.suppressedVisualEffects + ")";
+    }
+
     private ZenModeConfig getMutedRingerConfig() {
         ZenModeConfig config = new ZenModeConfig();
 
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 57587f7..9af0021 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -61,6 +61,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -80,19 +81,6 @@
                     ? Set.of("version", "manualRule", "automaticRules", "deletedRules")
                     : Set.of("version", "manualRule", "automaticRules");
 
-    // Differences for flagged fields are only generated if the flag is enabled.
-    // "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",
-                            "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", "disabledOrigin");
-
     // allowPriorityChannels is flagged by android.app.modes_api
     public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS =
             Set.of("allowPriorityChannels");
@@ -102,8 +90,7 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(
-                FLAG_MODES_UI);
+        return FlagsParameterization.progressionOf(FLAG_MODES_API, FLAG_MODES_UI);
     }
 
     public ZenModeDiffTest(FlagsParameterization flags) {
@@ -140,7 +127,7 @@
         ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
         ArrayMap<String, Object> expectedTo = new ArrayMap<>();
         List<Field> fieldsForDiff = getFieldsForDiffCheck(
-                ZenModeConfig.ZenRule.class, ZEN_RULE_EXEMPT_FIELDS);
+                ZenModeConfig.ZenRule.class, getZenRuleExemptFields());
         generateFieldDiffs(r1, r2, fieldsForDiff, expectedFrom, expectedTo);
 
         ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2);
@@ -158,6 +145,25 @@
         }
     }
 
+    private static Set<String> getZenRuleExemptFields() {
+        // "Metadata" fields are never compared.
+        Set<String> exemptFields = new LinkedHashSet<>(
+                Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
+                        "zenDeviceEffectsUserModifiedFields", "deletionInstant", "disabledOrigin"));
+        // Flagged fields are only compared if their flag is on.
+        if (!Flags.modesApi()) {
+            exemptFields.addAll(
+                    Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
+                            RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
+                            RuleDiff.FIELD_ZEN_DEVICE_EFFECTS,
+                            RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS));
+        }
+        if (!(Flags.modesApi() && Flags.modesUi())) {
+            exemptFields.add(RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS);
+        }
+        return exemptFields;
+    }
+
     @Test
     public void testConfigDiff_addRemoveSame() {
         // Default config, will test add, remove, and no change