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

Commit 485e2c88 authored by Lucas Silva's avatar Lucas Silva Committed by Android (Google) Code Review
Browse files

Merge "Update HomeControlsRemoteService to cleanup callbacks on destroy" into main

parents 9f153823 ed6b1047
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