Remove obsolete "Gaming" zen rule

SystemUI used to create this AutomaticZenRule, but stopped doing so around Android S or thereabouts, and was left over in some devices (or any newer devices that restore from such backups).

Fixes: 372705480
Test: ZenModesCleanupTest
Flag: android.app.modes_ui
Change-Id: Idf49dd8d67d00d3d7ce904f552e4a1092478c775
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt
new file mode 100644
index 0000000..4a53a7a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.AutomaticZenRule
+import android.app.NotificationManager
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ZenModesCleanupStartableTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    @Mock private lateinit var notificationManager: NotificationManager
+
+    private lateinit var underTest: ZenModesCleanupStartable
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            ZenModesCleanupStartable(
+                testScope.backgroundScope,
+                kosmos.backgroundCoroutineContext,
+                notificationManager,
+            )
+    }
+
+    @Test
+    fun start_withGamingModeZenRule_deletesIt() =
+        testScope.runTest {
+            whenever(notificationManager.automaticZenRules)
+                .thenReturn(
+                    mutableMapOf(
+                        Pair(
+                            "gaming",
+                            AutomaticZenRule.Builder(
+                                    "Gaming Mode",
+                                    Uri.parse(
+                                        "android-app://com.android.systemui/game-mode-dnd-controller"
+                                    ),
+                                )
+                                .setPackage("com.android.systemui")
+                                .build(),
+                        ),
+                        Pair(
+                            "other",
+                            AutomaticZenRule.Builder("Other Mode", Uri.parse("something-else"))
+                                .setPackage("com.other.package")
+                                .build(),
+                        ),
+                    )
+                )
+
+            underTest.start()
+            runCurrent()
+
+            verify(notificationManager).removeAutomaticZenRule(eq("gaming"))
+        }
+
+    @Test
+    fun start_withoutGamingModeZenRule_doesNothing() =
+        testScope.runTest {
+            whenever(notificationManager.automaticZenRules)
+                .thenReturn(
+                    mutableMapOf(
+                        Pair(
+                            "other",
+                            AutomaticZenRule.Builder("Other Mode", Uri.parse("something-else"))
+                                .setPackage("com.android.systemui")
+                                .build(),
+                        )
+                    )
+                )
+
+            underTest.start()
+            runCurrent()
+
+            verify(notificationManager, never()).removeAutomaticZenRule(any())
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ca5f49d..684ce48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -88,6 +88,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModesCleanupStartable;
 
 import dagger.Binds;
 import dagger.Module;
@@ -299,4 +300,10 @@
             ZenModeRepository repository) {
         return new NotificationsSoundPolicyInteractor(repository);
     }
+
+    /** Binds {@link ZenModesCleanupStartable} as a {@link CoreStartable}. */
+    @Binds
+    @IntoMap
+    @ClassKey(ZenModesCleanupStartable.class)
+    CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt
new file mode 100644
index 0000000..32b476b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.NotificationManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.modes.shared.ModesUi
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Cleanup task that deletes the obsolete "Gaming" AutomaticZenRule that was created by SystemUI in
+ * the faraway past, and still exists on some devices through upgrades or B&R.
+ */
+// TODO: b/372874878 - Remove this thing once it has run long enough
+class ZenModesCleanupStartable
+@Inject
+constructor(
+    @Application private val applicationCoroutineScope: CoroutineScope,
+    @Background private val bgContext: CoroutineContext,
+    val notificationManager: NotificationManager,
+) : CoreStartable {
+
+    override fun start() {
+        if (!ModesUi.isEnabled) {
+            return
+        }
+        applicationCoroutineScope.launch { deleteObsoleteGamingMode() }
+    }
+
+    private suspend fun deleteObsoleteGamingMode() {
+        withContext(bgContext) {
+            val allRules = notificationManager.automaticZenRules
+            val gamingModeEntry =
+                allRules.entries.firstOrNull { entry ->
+                    entry.value.packageName == "com.android.systemui" &&
+                        entry.value.conditionId?.toString() ==
+                            "android-app://com.android.systemui/game-mode-dnd-controller"
+                }
+            if (gamingModeEntry != null) {
+                notificationManager.removeAutomaticZenRule(gamingModeEntry.key)
+            }
+        }
+    }
+}