Verify rule ownership in setAutomaticZenRuleState

Implementation now matches API documentation -- apps can only activate/deactivate their own rules.

Also refactored applyGlobalZenModeAsImplicitZenRule slightly to remove repetition and call setAutomaticZenRuleStateLocked directly (doesn't need the rule ownership check since we already know it's the implicit rule for that package).

Fixes: 326199494
Test: atest ZenModeHelperTest NotificationManagerZenTest
Change-Id: I4efecaafb7798dc72bf0955c1b1604c92c8f27e8
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index efb8c84..912b066 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -123,6 +123,7 @@
 import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -593,20 +594,20 @@
             if (mConfig == null) {
                 return;
             }
+            ZenModeConfig newConfig = mConfig.copy();
+            ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
             if (zenMode == Global.ZEN_MODE_OFF) {
                 // Deactivate implicit rule if it exists and is active; otherwise ignore.
-                ZenRule rule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
                 if (rule != null) {
                     Condition deactivated = new Condition(rule.conditionId,
                             mContext.getString(R.string.zen_mode_implicit_deactivated),
                             Condition.STATE_FALSE);
-                    setAutomaticZenRuleState(rule.id, deactivated, UPDATE_ORIGIN_APP, callingUid);
+                    setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+                            deactivated, UPDATE_ORIGIN_APP, callingUid);
                 }
             } else {
                 // Either create a new rule with a default ZenPolicy, or update an existing rule's
                 // filter value. In both cases, also activate (and unsnooze) it.
-                ZenModeConfig newConfig = mConfig.copy();
-                ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
                 if (rule == null) {
                     rule = newImplicitZenRule(callingPkg);
 
@@ -878,9 +879,17 @@
             if (mConfig == null) return;
 
             newConfig = mConfig.copy();
-            ArrayList<ZenRule> rules = new ArrayList<>();
-            rules.add(newConfig.automaticRules.get(id));
-            setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+            ZenRule rule = newConfig.automaticRules.get(id);
+            if (Flags.modesApi()) {
+                if (rule != null && canManageAutomaticZenRule(rule)) {
+                    setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+                            condition, origin, callingUid);
+                }
+            } else {
+                ArrayList<ZenRule> rules = new ArrayList<>();
+                rules.add(rule); // rule may be null and throw NPE in the next method.
+                setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+            }
         }
     }
 
@@ -892,9 +901,15 @@
             if (mConfig == null) return;
             newConfig = mConfig.copy();
 
-            setAutomaticZenRuleStateLocked(newConfig,
-                    findMatchingRules(newConfig, ruleDefinition, condition),
-                    condition, origin, callingUid);
+            List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
+            if (Flags.modesApi()) {
+                for (int i = matchingRules.size() - 1; i >= 0; i--) {
+                    if (!canManageAutomaticZenRule(matchingRules.get(i))) {
+                        matchingRules.remove(i);
+                    }
+                }
+            }
+            setAutomaticZenRuleStateLocked(newConfig, matchingRules, condition, origin, callingUid);
         }
     }
 
@@ -914,8 +929,9 @@
         }
     }
 
-    private List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id, Condition condition) {
-        List<ZenRule> matchingRules= new ArrayList<>();
+    private static List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id,
+            Condition condition) {
+        List<ZenRule> matchingRules = new ArrayList<>();
         if (ruleMatches(id, condition, config.manualRule)) {
             matchingRules.add(config.manualRule);
         } else {
@@ -928,7 +944,7 @@
         return matchingRules;
     }
 
-    private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
+    private static boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
         if (id == null || rule == null || rule.conditionId == null) return false;
         if (!rule.conditionId.equals(id)) return false;
         if (Objects.equals(condition, rule.condition)) return false;
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 3f2ccaf..8c50ef4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -260,6 +260,7 @@
     AppOpsManager mAppOps;
     TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
     ZenModeEventLoggerFake mZenModeEventLogger;
+    private String mPkg;
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
@@ -270,7 +271,7 @@
         mContentResolver = mContext.getContentResolver();
         mResources = mock(Resources.class, withSettings()
                 .spiedInstance(mContext.getResources()));
-        String pkg = mContext.getPackageName();
+        mPkg = mContext.getPackageName();
         try {
             when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
                     getDefaultConfigParser());
@@ -301,14 +302,14 @@
         when(mPackageManager.getPackageUidAsUser(eq(CUSTOM_PKG_NAME), anyInt()))
                 .thenReturn(CUSTOM_PKG_UID);
         when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
-                new String[]{pkg});
+                new String[]{mPkg});
 
         ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
         appInfoSpy.icon = ICON_RES_ID;
         when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
         when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
                 .thenReturn(appInfoSpy);
-        when(mPackageManager.getApplicationInfo(eq(mContext.getPackageName()), anyInt()))
+        when(mPackageManager.getApplicationInfo(eq(mPkg), anyInt()))
                 .thenReturn(appInfoSpy);
         mZenModeHelper.mPm = mPackageManager;
 
@@ -2512,16 +2513,16 @@
         scheduleInfo.endHour = 1;
         Uri sharedUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
         AutomaticZenRule zenRule = new AutomaticZenRule("name",
-                new ComponentName("android", "ScheduleConditionProvider"),
+                new ComponentName(mPkg, "ScheduleConditionProvider"),
                 sharedUri,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
+        String id = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule,
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
         AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
-                new ComponentName("android", "ScheduleConditionProvider"),
+                new ComponentName(mPkg, "ScheduleConditionProvider"),
                 sharedUri,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-        String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2,
+        String id2 = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule2,
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
 
         Condition condition = new Condition(sharedUri, "", STATE_TRUE);
@@ -2531,11 +2532,11 @@
         for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
             if (rule.id.equals(id)) {
                 assertNotNull(rule.condition);
-                assertTrue(rule.condition.state == STATE_TRUE);
+                assertEquals(STATE_TRUE, rule.condition.state);
             }
             if (rule.id.equals(id2)) {
                 assertNotNull(rule.condition);
-                assertTrue(rule.condition.state == STATE_TRUE);
+                assertEquals(STATE_TRUE, rule.condition.state);
             }
         }
 
@@ -2546,11 +2547,11 @@
         for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
             if (rule.id.equals(id)) {
                 assertNotNull(rule.condition);
-                assertTrue(rule.condition.state == STATE_FALSE);
+                assertEquals(STATE_FALSE, rule.condition.state);
             }
             if (rule.id.equals(id2)) {
                 assertNotNull(rule.condition);
-                assertTrue(rule.condition.state == STATE_FALSE);
+                assertEquals(STATE_FALSE, rule.condition.state);
             }
         }
     }
@@ -3637,14 +3638,14 @@
         AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
                 .setType(TYPE_BEDTIME)
                 .build();
-        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP,
+        String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(mPkg, bedtime, UPDATE_ORIGIN_APP,
                 "reason", CUSTOM_PKG_UID);
 
         // Create immersive rule
         AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
                 .setType(TYPE_IMMERSIVE)
                 .build();
-        String immersiveId = mZenModeHelper.addAutomaticZenRule("pkg", immersive, UPDATE_ORIGIN_APP,
+        String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP,
                 "reason", CUSTOM_PKG_UID);
 
         // Event 2: Activate bedtime rule
@@ -5434,6 +5435,48 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
+        // Assume existence of an other-package-owned rule that is currently ACTIVE.
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+        ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+        otherRule.zenMode = ZEN_MODE_ALARMS;
+        otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
+        ZenModeConfig config = mZenModeHelper.mConfig.copy();
+        config.automaticRules.put("otherRule", otherRule);
+        mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+        // Should be ignored.
+        mZenModeHelper.setAutomaticZenRuleState("otherRule",
+                new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
+                UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
+        // Assume existence of an other-package-owned rule that is currently ACTIVE.
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+        ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+        otherRule.zenMode = ZEN_MODE_ALARMS;
+        otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
+        ZenModeConfig config = mZenModeHelper.mConfig.copy();
+        config.automaticRules.put("otherRule", otherRule);
+        mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+        // Should be ignored.
+        mZenModeHelper.setAutomaticZenRuleState(otherRule.conditionId,
+                new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
+                UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+    }
+
+    @Test
     @EnableFlags(android.app.Flags.FLAG_MODES_API)
     public void testCallbacks_policy() throws Exception {
         setupZenConfig();
@@ -5583,13 +5626,13 @@
     public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
                 .isEqualTo(STATE_TRUE);
 
-        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
                 ZEN_MODE_OFF);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)