BaseActivatable

Replaces SafeActivatable with BaseActivatable which is "safe" like
before but can also manage child activatables.

Bug: 354269846
Test: unit tests added
Test: manually tested with the next CL
Flag: com.android.systemui.scene_container
Change-Id: Ifaa6d5babe164a7ecb332d7dba247508ea6fed64
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/BaseActivatableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/BaseActivatableTest.kt
new file mode 100644
index 0000000..f6f58c9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/BaseActivatableTest.kt
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.lifecycle
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BaseActivatableTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val underTest = FakeActivatable()
+
+    @Test
+    fun activate() =
+        testScope.runTest {
+            assertThat(underTest.isActive).isFalse()
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+        }
+
+    @Test
+    fun activate_andCancel() =
+        testScope.runTest {
+            assertThat(underTest.isActive).isFalse()
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            val job = Job()
+            underTest.activateIn(testScope, context = job)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.isActive).isFalse()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(1)
+        }
+
+    @Test
+    fun activate_afterCancellation() =
+        testScope.runTest {
+            assertThat(underTest.isActive).isFalse()
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            val job = Job()
+            underTest.activateIn(testScope, context = job)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.isActive).isFalse()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(1)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(underTest.activationCount).isEqualTo(2)
+            assertThat(underTest.cancellationCount).isEqualTo(1)
+        }
+
+    @Test(expected = IllegalStateException::class)
+    fun activate_whileActive_throws() =
+        testScope.runTest {
+            assertThat(underTest.isActive).isFalse()
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+        }
+
+    @Test
+    fun addChild_beforeActive_activatesChildrenOnceActivated() =
+        testScope.runTest {
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            assertThat(underTest.isActive).isFalse()
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.activateIn(this)
+            runCurrent()
+
+            assertThat(underTest.isActive).isTrue()
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+        }
+
+    @Test
+    fun addChild_whileActive_activatesChildrenImmediately() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            runCurrent()
+
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+        }
+
+    @Test
+    fun addChild_afterCancellation_doesNotActivateChildren() =
+        testScope.runTest {
+            val job = Job()
+            underTest.activateIn(this, context = job)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.isActive).isFalse()
+
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            runCurrent()
+
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+        }
+
+    @Test
+    fun activate_cancellation_cancelsCurrentChildren() =
+        testScope.runTest {
+            val job = Job()
+            underTest.activateIn(this, context = job)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            runCurrent()
+
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+        }
+
+    @Test
+    fun activate_afterCancellation_reactivatesCurrentChildren() =
+        testScope.runTest {
+            val job = Job()
+            underTest.activateIn(this, context = job)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            runCurrent()
+
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.activateIn(this)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+        }
+
+    @Test
+    fun removeChild_beforeActive_neverActivatesChild() =
+        testScope.runTest {
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            assertThat(underTest.isActive).isFalse()
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+        }
+
+    @Test
+    fun removeChild_whileActive_cancelsChild() =
+        testScope.runTest {
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            assertThat(underTest.isActive).isFalse()
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.activateIn(this)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+
+            underTest.removeChild(child1)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isTrue()
+        }
+
+    @Test
+    fun removeChild_afterCancellation_doesNotReactivateChildren() =
+        testScope.runTest {
+            val child1 = FakeActivatable()
+            val child2 = FakeActivatable()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            assertThat(underTest.isActive).isFalse()
+            underTest.addChild(child1)
+            underTest.addChild(child2)
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            val job = Job()
+            underTest.activateIn(this, context = job)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(child1.isActive).isTrue()
+            assertThat(child2.isActive).isTrue()
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.isActive).isFalse()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isFalse()
+
+            underTest.removeChild(child1)
+            underTest.activateIn(this)
+            runCurrent()
+            assertThat(underTest.isActive).isTrue()
+            assertThat(child1.isActive).isFalse()
+            assertThat(child2.isActive).isTrue()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SafeActivatableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SafeActivatableTest.kt
deleted file mode 100644
index 9484821..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SafeActivatableTest.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.lifecycle
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SafeActivatableTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private val underTest = FakeActivatable()
-
-    @Test
-    fun activate() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-        }
-
-    @Test
-    fun activate_andCancel() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            val job = Job()
-            underTest.activateIn(testScope, context = job)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            job.cancel()
-            runCurrent()
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(1)
-        }
-
-    @Test
-    fun activate_afterCancellation() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            val job = Job()
-            underTest.activateIn(testScope, context = job)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            job.cancel()
-            runCurrent()
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(1)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(2)
-            assertThat(underTest.cancellationCount).isEqualTo(1)
-        }
-
-    @Test(expected = IllegalStateException::class)
-    fun activate_whileActive_throws() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-        }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/BaseActivatable.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/BaseActivatable.kt
new file mode 100644
index 0000000..03476ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/BaseActivatable.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.lifecycle
+
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+/**
+ * A base [Activatable] with the following characteristics:
+ * 1. **Can be concurrently activated by no more than one owner.** A previous call to [activate]
+ *    must be canceled before a new call to [activate] can be made. Trying to call [activate] while
+ *    already active will fail with an error
+ * 2. **Can manage child [Activatable]s**. See [addChild] and [removeChild]. Added children
+ *    automatically track the activation state of the parent such that when the parent is active,
+ *    the children are active and vice-versa. Children are also retained such that deactivating the
+ *    parent and reactivating it also cancels and reactivates the children.
+ */
+abstract class BaseActivatable : Activatable {
+
+    private val _isActive = AtomicBoolean(false)
+
+    var isActive: Boolean
+        get() = _isActive.get()
+        private set(value) {
+            _isActive.set(value)
+        }
+
+    final override suspend fun activate(): Nothing {
+        val allowed = _isActive.compareAndSet(false, true)
+        check(allowed) { "Cannot activate an already active activatable!" }
+
+        coroutineScope {
+            try {
+                launch { manageChildren() }
+                onActivated()
+            } finally {
+                isActive = false
+            }
+        }
+    }
+
+    /**
+     * Notifies that the [Activatable] has been activated.
+     *
+     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
+     * its state fresh and/or perform side-effects.
+     *
+     * The method suspends and doesn't return until all work required by the object is finished. In
+     * most cases, it's expected for the work to remain ongoing forever so this method will forever
+     * suspend its caller until the coroutine that called it is canceled.
+     *
+     * Implementations could follow this pattern:
+     * ```kotlin
+     * override suspend fun onActivated(): Nothing {
+     *     coroutineScope {
+     *         launch { ... }
+     *         launch { ... }
+     *         launch { ... }
+     *     }
+     * }
+     * ```
+     *
+     * @see activate
+     */
+    protected abstract suspend fun onActivated(): Nothing
+
+    private val newChildren = Channel<Activatable>(Channel.BUFFERED)
+    private val jobByChild: MutableMap<Activatable, Job> by lazy { mutableMapOf() }
+
+    private suspend fun manageChildren(): Nothing {
+        coroutineScope {
+            // Reactivate children that were added during a previous activation:
+            jobByChild.keys.forEach { child -> jobByChild[child] = launch { child.activate() } }
+
+            // Process requests to add more children:
+            newChildren.receiveAsFlow().collect { newChild ->
+                removeChildInternal(newChild)
+                jobByChild[newChild] = launch { newChild.activate() }
+            }
+
+            awaitCancellation()
+        }
+    }
+
+    fun addChild(child: Activatable) {
+        newChildren.trySend(child)
+    }
+
+    fun removeChild(child: Activatable) {
+        removeChildInternal(child)
+    }
+
+    private fun removeChildInternal(child: Activatable) {
+        jobByChild.remove(child)?.cancel()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SafeActivatable.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SafeActivatable.kt
deleted file mode 100644
index 4dd76f8..0000000
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SafeActivatable.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.lifecycle
-
-import java.util.concurrent.atomic.AtomicBoolean
-
-/**
- * An [Activatable] that can be concurrently activated by no more than one owner.
- *
- * A previous call to [activate] must be canceled before a new call to [activate] can be made.
- * Trying to call [activate] while already active will fail with an error.
- */
-abstract class SafeActivatable : Activatable {
-
-    private val _isActive = AtomicBoolean(false)
-
-    var isActive: Boolean
-        get() = _isActive.get()
-        private set(value) {
-            _isActive.set(value)
-        }
-
-    final override suspend fun activate(): Nothing {
-        val allowed = _isActive.compareAndSet(false, true)
-        check(allowed) { "Cannot activate an already active activatable!" }
-
-        try {
-            onActivated()
-        } finally {
-            isActive = false
-        }
-    }
-
-    /**
-     * Notifies that the [Activatable] has been activated.
-     *
-     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
-     * its state fresh and/or perform side-effects.
-     *
-     * The method suspends and doesn't return until all work required by the object is finished. In
-     * most cases, it's expected for the work to remain ongoing forever so this method will forever
-     * suspend its caller until the coroutine that called it is canceled.
-     *
-     * Implementations could follow this pattern:
-     * ```kotlin
-     * override suspend fun onActivated(): Nothing {
-     *     coroutineScope {
-     *         launch { ... }
-     *         launch { ... }
-     *         launch { ... }
-     *     }
-     * }
-     * ```
-     *
-     * @see activate
-     */
-    protected abstract suspend fun onActivated(): Nothing
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
index 2edde4a..104b076 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
@@ -23,7 +23,7 @@
 import kotlinx.coroutines.launch
 
 /** Base class for all System UI view-models. */
-abstract class SysUiViewModel : SafeActivatable() {
+abstract class SysUiViewModel : BaseActivatable() {
 
     override suspend fun onActivated(): Nothing {
         awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
index ef9f8ff..ae0061b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -19,7 +19,7 @@
 import android.util.IndentingPrintWriter
 import com.android.systemui.Dumpable
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.lifecycle.SafeActivatable
+import com.android.systemui.lifecycle.BaseActivatable
 import com.android.systemui.lifecycle.SysUiViewModel
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.printCollection
@@ -189,7 +189,7 @@
 ) : SimpleFlowDumper(), ActivatableFlowDumper {
 
     private val registration =
-        object : SafeActivatable() {
+        object : BaseActivatable() {
             override suspend fun onActivated(): Nothing {
                 try {
                     dumpManager.registerCriticalDumpable(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt
index bcc7393..4c05939 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt
@@ -21,7 +21,7 @@
 class FakeActivatable(
     private val onActivation: () -> Unit = {},
     private val onDeactivation: () -> Unit = {},
-) : SafeActivatable() {
+) : BaseActivatable() {
     var activationCount = 0
     var cancellationCount = 0