Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed6b1047 authored by Lucas Silva's avatar Lucas Silva
Browse files

Update HomeControlsRemoteService to cleanup callbacks on destroy

Flag: com.android.systemui.home_controls_dream_hsum
Bug: 370691405
Test: atest HomeControlsRemoteServiceBinderTest
Change-Id: Ieafa63e58c89e8184722ef99af9ae6cedcfecd94
parent 067c153f
Loading
Loading
Loading
Loading
+53 −39
Original line number Diff line number Diff line
@@ -31,7 +31,8 @@ import com.android.systemui.controls.panels.authorizedPanelsRepository
import com.android.systemui.controls.panels.selectedComponentRepository
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
import com.android.systemui.dreams.homecontrols.shared.controlsSettings
import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsComponent
import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsListingController
import com.android.systemui.dreams.homecontrols.system.domain.interactor.homeControlsComponentInteractor
@@ -42,13 +43,10 @@ import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -56,6 +54,7 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -90,13 +89,13 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
    fun testRegisterSingleListener() =
        testScope.runTest {
            setup()
            val controlsSettings by collectLastValue(addCallback())
            val controlsSettings by collectLastValue(underTest.controlsSettings)
            runServicesUpdate()

            assertThat(controlsSettings)
                .isEqualTo(
                    CallbackArgs(
                        panelComponent = TEST_COMPONENT,
                    HomeControlsComponentInfo(
                        componentName = TEST_COMPONENT,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )
@@ -106,21 +105,21 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
    fun testRegisterMultipleListeners() =
        testScope.runTest {
            setup()
            val controlsSettings1 by collectLastValue(addCallback())
            val controlsSettings2 by collectLastValue(addCallback())
            val controlsSettings1 by collectLastValue(underTest.controlsSettings)
            val controlsSettings2 by collectLastValue(underTest.controlsSettings)
            runServicesUpdate()

            assertThat(controlsSettings1)
                .isEqualTo(
                    CallbackArgs(
                        panelComponent = TEST_COMPONENT,
                    HomeControlsComponentInfo(
                        componentName = TEST_COMPONENT,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )
            assertThat(controlsSettings2)
                .isEqualTo(
                    CallbackArgs(
                        panelComponent = TEST_COMPONENT,
                    HomeControlsComponentInfo(
                        componentName = TEST_COMPONENT,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )
@@ -130,13 +129,13 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
    fun testListenerCalledWhenStateChanges() =
        testScope.runTest {
            setup()
            val controlsSettings by collectLastValue(addCallback())
            val controlsSettings by collectLastValue(underTest.controlsSettings)
            runServicesUpdate()

            assertThat(controlsSettings)
                .isEqualTo(
                    CallbackArgs(
                        panelComponent = TEST_COMPONENT,
                    HomeControlsComponentInfo(
                        componentName = TEST_COMPONENT,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )
@@ -146,13 +145,47 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
            // Updated with null component now that we are no longer authorized.
            assertThat(controlsSettings)
                .isEqualTo(
                    CallbackArgs(panelComponent = null, allowTrivialControlsOnLockscreen = false)
                    HomeControlsComponentInfo(
                        componentName = null,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )
        }

    @Test
    fun testDestroy() =
        testScope.runTest {
            setup()
            val controlsSettings1 by collectLastValue(underTest.controlsSettings)

            assertThat(controlsSettings1)
                .isEqualTo(
                    HomeControlsComponentInfo(
                        componentName = null,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )

            underTest.onDestroy()
            runServicesUpdate()
            fakeControlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)

            // Existing callback is not triggered if destroyed.
            assertThat(controlsSettings1)
                .isEqualTo(
                    HomeControlsComponentInfo(
                        componentName = null,
                        allowTrivialControlsOnLockscreen = false,
                    )
                )
            // New callbacks cannot be added.
            val controlsSettings2 by collectLastValue(underTest.controlsSettings)
            assertThat(controlsSettings2).isNull()
        }

    private fun TestScope.runServicesUpdate() {
        runCurrent()
        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
        val listings = listOf(buildControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
        val callback = withArgCaptor {
            Mockito.verify(kosmos.controlsListingController).addCallback(capture())
        }
@@ -160,20 +193,6 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
        runCurrent()
    }

    private fun addCallback() = conflatedCallbackFlow {
        val callback =
            object : IOnControlsSettingsChangeListener.Stub() {
                override fun onControlsSettingsChanged(
                    panelComponent: ComponentName?,
                    allowTrivialControlsOnLockscreen: Boolean,
                ) {
                    trySend(CallbackArgs(panelComponent, allowTrivialControlsOnLockscreen))
                }
            }
        underTest.registerListenerForCurrentUser(callback)
        awaitClose { underTest.unregisterListenerForCurrentUser(callback) }
    }

    private suspend fun TestScope.setup() {
        kosmos.fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
        kosmos.fakeUserTracker.set(listOf(PRIMARY_USER), 0)
@@ -182,12 +201,7 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
        runCurrent()
    }

    private data class CallbackArgs(
        val panelComponent: ComponentName?,
        val allowTrivialControlsOnLockscreen: Boolean,
    )

    private fun ControlsServiceInfo(
    private fun buildControlsServiceInfo(
        componentName: ComponentName,
        label: CharSequence,
        hasPanel: Boolean,
@@ -225,7 +239,7 @@ class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
            UserInfo(
                /* id= */ PRIMARY_USER_ID,
                /* name= */ "primary user",
                /* flags= */ UserInfo.FLAG_PRIMARY,
                /* flags= */ UserInfo.FLAG_MAIN,
            )

        private const val TEST_PACKAGE = "pkg"
+2 −28
Original line number Diff line number Diff line
@@ -16,20 +16,16 @@

package com.android.systemui.dreams.homecontrols.service

import android.content.ComponentName
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy
import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
import com.android.systemui.dreams.homecontrols.shared.controlsSettings
import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -45,30 +41,8 @@ constructor(
    @Assisted private val proxy: IHomeControlsRemoteProxy,
) : FlowDumperImpl(dumpManager) {

    private companion object {
        const val TAG = "HomeControlsRemoteProxy"
    }

    val componentInfo: Flow<HomeControlsComponentInfo> =
        conflatedCallbackFlow {
                val listener =
                    object : IOnControlsSettingsChangeListener.Stub() {
                        override fun onControlsSettingsChanged(
                            panelComponent: ComponentName?,
                            allowTrivialControlsOnLockscreen: Boolean,
                        ) {
                            trySendWithFailureLogging(
                                HomeControlsComponentInfo(
                                    panelComponent,
                                    allowTrivialControlsOnLockscreen,
                                ),
                                TAG,
                            )
                        }
                    }
                proxy.registerListenerForCurrentUser(listener)
                awaitClose { proxy.unregisterListenerForCurrentUser(listener) }
            }
        proxy.controlsSettings
            .distinctUntilChanged()
            .stateIn(bgScope, SharingStarted.WhileSubscribed(), null)
            .dumpValue("componentInfo")
+40 −0
Original line number Diff line number Diff line
/*
 * 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.dreams.homecontrols.shared

import android.content.ComponentName
import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow

val IHomeControlsRemoteProxy.controlsSettings: Flow<HomeControlsComponentInfo>
    get() = conflatedCallbackFlow {
        val listener =
            object : IOnControlsSettingsChangeListener.Stub() {
                override fun onControlsSettingsChanged(
                    panelComponent: ComponentName?,
                    allowTrivialControlsOnLockscreen: Boolean,
                ) {
                    trySend(
                        HomeControlsComponentInfo(panelComponent, allowTrivialControlsOnLockscreen)
                    )
                }
            }
        registerListenerForCurrentUser(listener)
        awaitClose { unregisterListenerForCurrentUser(listener) }
    }
+13 −0
Original line number Diff line number Diff line
@@ -57,6 +57,11 @@ constructor(binderFactory: HomeControlsRemoteServiceBinder.Factory) : LifecycleS
        super.onBind(intent)
        return binder
    }

    override fun onDestroy() {
        super.onDestroy()
        binder.onDestroy()
    }
}

class HomeControlsRemoteServiceBinder
@@ -148,6 +153,14 @@ constructor(
        }
    }

    fun onDestroy() {
        logger.d("Service destroyed")
        callbacks.kill()
        callbackCount.set(0)
        collectionJob?.cancel()
        collectionJob = null
    }

    @AssistedFactory
    interface Factory {
        fun create(lifecycleOwner: LifecycleOwner): HomeControlsRemoteServiceBinder