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

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

Merge changes If98cf28a,I863d80d2 into main

* changes:
  Implement retry logic for home panel dream
  Implement PackageChangeInteractor for listening to package updates.
parents da8036c6 34da0b8d
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.data.shared.model.PackageChangeModel
import com.android.systemui.common.shared.model.PackageChangeModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -32,6 +32,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -67,7 +68,8 @@ class PackageChangeRepositoryTest : SysuiTestCase() {
                        scope = applicationCoroutineScope,
                        context = context,
                        bgHandler = handler,
                        logger = PackageUpdateLogger(logcatLogBuffer())
                        logger = PackageUpdateLogger(logcatLogBuffer()),
                        systemClock = fakeSystemClock,
                    )
                updateMonitor
            }
+30 −7
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.data.shared.model.PackageChangeModel
import com.android.systemui.common.shared.model.PackageChangeModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -32,6 +32,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -71,8 +72,11 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
                    bgHandler = handler,
                    context = context,
                    scope = applicationCoroutineScope,
                    logger = PackageUpdateLogger(logcatLogBuffer())
                    logger = PackageUpdateLogger(logcatLogBuffer()),
                    systemClock = fakeSystemClock,
                )

            fakeSystemClock.setCurrentTimeMillis(0)
        }

    @Test
@@ -96,11 +100,16 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
                val packageChange by collectLastValue(monitor.packageChanged)
                assertThat(packageChange).isNull()

                fakeSystemClock.setCurrentTimeMillis(100)
                monitor.onPackageAdded(TEST_PACKAGE, 123)

                assertThat(packageChange)
                    .isEqualTo(
                        PackageChangeModel.Installed(packageName = TEST_PACKAGE, packageUid = 123)
                        PackageChangeModel.Installed(
                            packageName = TEST_PACKAGE,
                            packageUid = 123,
                            timeMillis = 100,
                        )
                    )
            }
        }
@@ -112,11 +121,16 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
                val packageChange by collectLastValue(monitor.packageChanged)
                assertThat(packageChange).isNull()

                fakeSystemClock.setCurrentTimeMillis(200)
                monitor.onPackageRemoved(TEST_PACKAGE, 123)

                assertThat(packageChange)
                    .isEqualTo(
                        PackageChangeModel.Uninstalled(packageName = TEST_PACKAGE, packageUid = 123)
                        PackageChangeModel.Uninstalled(
                            packageName = TEST_PACKAGE,
                            packageUid = 123,
                            timeMillis = 200,
                        )
                    )
            }
        }
@@ -128,11 +142,16 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
                val packageChange by collectLastValue(monitor.packageChanged)
                assertThat(packageChange).isNull()

                fakeSystemClock.setCurrentTimeMillis(100)
                monitor.onPackageChanged(TEST_PACKAGE, 123, emptyArray())

                assertThat(packageChange)
                    .isEqualTo(
                        PackageChangeModel.Changed(packageName = TEST_PACKAGE, packageUid = 123)
                        PackageChangeModel.Changed(
                            packageName = TEST_PACKAGE,
                            packageUid = 123,
                            timeMillis = 100,
                        )
                    )
            }
        }
@@ -144,13 +163,15 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
                val packageChange by collectLastValue(monitor.packageChanged)
                assertThat(packageChange).isNull()

                fakeSystemClock.setCurrentTimeMillis(100)
                monitor.onPackageUpdateStarted(TEST_PACKAGE, 123)

                assertThat(packageChange)
                    .isEqualTo(
                        PackageChangeModel.UpdateStarted(
                            packageName = TEST_PACKAGE,
                            packageUid = 123
                            packageUid = 123,
                            timeMillis = 100,
                        )
                    )
            }
@@ -163,13 +184,15 @@ class PackageUpdateMonitorTest : SysuiTestCase() {
                val packageChange by collectLastValue(monitor.packageChanged)
                assertThat(packageChange).isNull()

                fakeSystemClock.setCurrentTimeMillis(100)
                monitor.onPackageUpdateFinished(TEST_PACKAGE, 123)

                assertThat(packageChange)
                    .isEqualTo(
                        PackageChangeModel.UpdateFinished(
                            packageName = TEST_PACKAGE,
                            packageUid = 123
                            packageUid = 123,
                            timeMillis = 100,
                        )
                    )
            }
+175 −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.common.domain.interactor

import android.content.pm.UserInfo
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.data.repository.fakePackageChangeRepository
import com.android.systemui.common.data.repository.packageChangeRepository
import com.android.systemui.common.shared.model.PackageChangeModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class PackageChangeInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private lateinit var underTest: PackageChangeInteractor

    @Before
    fun setUp() =
        with(kosmos) {
            underTest =
                PackageChangeInteractor(
                    packageChangeRepository = packageChangeRepository,
                    userInteractor = selectedUserInteractor,
                )
            fakeUserRepository.setUserInfos(listOf(MAIN_USER, SECONDARY_USER))
        }

    @Test
    fun packageChanges() =
        with(kosmos) {
            testScope.runTest {
                val packageChange by collectLastValue(underTest.packageChanged(MAIN_USER_HANDLE))
                assertThat(packageChange).isNull()

                // Even if secondary user is active, we should still receive changes for the
                // primary user.
                setUser(SECONDARY_USER)

                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "pkg",
                        packageUid = UserHandle.getUid(MAIN_USER.id, /* appId = */ 10),
                    )
                )

                assertThat(packageChange).isInstanceOf(PackageChangeModel.Installed::class.java)
                assertThat(packageChange?.packageName).isEqualTo("pkg")
            }
        }

    @Test
    fun packageChanges_ignoresUpdatesFromOtherUsers() =
        with(kosmos) {
            testScope.runTest {
                val packageChange by collectLastValue(underTest.packageChanged(MAIN_USER_HANDLE))
                assertThat(packageChange).isNull()

                setUser(SECONDARY_USER)
                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "pkg",
                        packageUid = UserHandle.getUid(SECONDARY_USER.id, /* appId = */ 10),
                    )
                )

                assertThat(packageChange).isNull()
            }
        }

    @Test
    fun packageChanges_forCurrentUser() =
        with(kosmos) {
            testScope.runTest {
                val packageChanges by collectValues(underTest.packageChanged(UserHandle.CURRENT))
                assertThat(packageChanges).isEmpty()

                setUser(SECONDARY_USER)
                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "first",
                        packageUid = UserHandle.getUid(SECONDARY_USER.id, /* appId = */ 10),
                    )
                )
                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "second",
                        packageUid = UserHandle.getUid(MAIN_USER.id, /* appId = */ 10),
                    )
                )
                setUser(MAIN_USER)
                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "third",
                        packageUid = UserHandle.getUid(MAIN_USER.id, /* appId = */ 10),
                    )
                )

                assertThat(packageChanges.map { it.packageName })
                    .containsExactly("first", "third")
                    .inOrder()
            }
        }

    @Test
    fun packageChanges_forSpecificPackageName() =
        with(kosmos) {
            testScope.runTest {
                val packageChange by
                    collectLastValue(underTest.packageChanged(MAIN_USER_HANDLE, "mypkg"))
                assertThat(packageChange).isNull()

                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "other",
                        packageUid = UserHandle.getUid(MAIN_USER.id, /* appId = */ 10),
                    )
                )
                assertThat(packageChange).isNull()

                fakePackageChangeRepository.notifyChange(
                    PackageChangeModel.Installed(
                        packageName = "mypkg",
                        packageUid = UserHandle.getUid(MAIN_USER.id, /* appId = */ 10),
                    )
                )
                assertThat(packageChange).isInstanceOf(PackageChangeModel.Installed::class.java)
                assertThat(packageChange?.packageName).isEqualTo("mypkg")
            }
        }

    private suspend fun TestScope.setUser(user: UserInfo) {
        kosmos.fakeUserRepository.setSelectedUserInfo(user)
        runCurrent()
    }

    private companion object {
        val MAIN_USER_HANDLE = UserHandle.of(1)
        val MAIN_USER = UserInfo(MAIN_USER_HANDLE.identifier, "main", UserInfo.FLAG_MAIN)
        val SECONDARY_USER_HANDLE = UserHandle.of(2)
        val SECONDARY_USER = UserInfo(SECONDARY_USER_HANDLE.identifier, "secondary", 0)
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package com.android.systemui.dreams.homecontrols

import android.service.dream.dreamManager
import com.android.systemui.common.domain.interactor.packageChangeInteractor
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.panels.authorizedPanelsRepository
@@ -24,15 +26,19 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.fakeSystemClock

val Kosmos.homeControlsComponentInteractor by
    Kosmos.Fixture {
        HomeControlsComponentInteractor(
            selectedComponentRepository = selectedComponentRepository,
            controlsComponent,
            controlsComponent = controlsComponent,
            authorizedPanelsRepository = authorizedPanelsRepository,
            userRepository = fakeUserRepository,
            bgScope = applicationCoroutineScope,
            systemClock = fakeSystemClock,
            dreamManager = dreamManager,
            packageChangeInteractor = packageChangeInteractor,
        )
    }

+109 −37
Original line number Diff line number Diff line
@@ -20,37 +20,38 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.ServiceInfo
import android.content.pm.UserInfo
import android.os.UserHandle
import android.service.dream.dreamManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.data.repository.fakePackageChangeRepository
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.panels.SelectedComponentRepository
import com.android.systemui.controls.panels.authorizedPanelsRepository
import com.android.systemui.controls.panels.selectedComponentRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor.Companion.MAX_UPDATE_CORRELATION_DELAY
import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
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.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
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.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -59,36 +60,17 @@ class HomeControlsComponentInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    private lateinit var controlsComponent: ControlsComponent
    private lateinit var controlsListingController: ControlsListingController
    private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
    private lateinit var underTest: HomeControlsComponentInteractor
    private lateinit var userRepository: FakeUserRepository
    private lateinit var selectedComponentRepository: SelectedComponentRepository

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        controlsComponent = kosmos.controlsComponent
        authorizedPanelsRepository = kosmos.authorizedPanelsRepository
        controlsListingController = kosmos.controlsListingController
        selectedComponentRepository = kosmos.selectedComponentRepository

        userRepository = kosmos.fakeUserRepository
        userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))

    fun setUp() =
        with(kosmos) {
            fakeSystemClock.setCurrentTimeMillis(0)
            fakeUserRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
            whenever(controlsComponent.getControlsListingController())
                .thenReturn(Optional.of(controlsListingController))

        underTest =
            HomeControlsComponentInteractor(
                selectedComponentRepository,
                controlsComponent,
                authorizedPanelsRepository,
                userRepository,
                kosmos.applicationCoroutineScope,
            )
            underTest = homeControlsComponentInteractor
        }

    @Test
@@ -181,22 +163,112 @@ class HomeControlsComponentInteractorTest : SysuiTestCase() {
                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                whenever(controlsComponent.getControlsListingController())
                    .thenReturn(Optional.empty())
                userRepository.setSelectedUserInfo(PRIMARY_USER)
                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                val actualValue by collectLastValue(underTest.panelComponent)
                assertThat(actualValue).isNull()
            }
        }

    @Test
    fun testMonitoringUpdatesAndRestart() =
        with(kosmos) {
            testScope.runTest {
                setActiveUser(PRIMARY_USER)
                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                whenever(controlsListingController.getCurrentServices())
                    .thenReturn(
                        listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
                    )

                val job = launch { underTest.monitorUpdatesAndRestart() }
                val panelComponent by collectLastValue(underTest.panelComponent)

                assertThat(panelComponent).isEqualTo(TEST_COMPONENT)
                verify(dreamManager, never()).startDream()

                fakeSystemClock.advanceTime(100)
                // The package update is started.
                fakePackageChangeRepository.notifyUpdateStarted(
                    TEST_PACKAGE,
                    UserHandle.of(PRIMARY_USER_ID),
                )
                fakeSystemClock.advanceTime(MAX_UPDATE_CORRELATION_DELAY.inWholeMilliseconds)
                // Task fragment becomes empty as a result of the update.
                underTest.onTaskFragmentEmpty()

                runCurrent()
                verify(dreamManager, never()).startDream()

                fakeSystemClock.advanceTime(500)
                // The package update is finished.
                fakePackageChangeRepository.notifyUpdateFinished(
                    TEST_PACKAGE,
                    UserHandle.of(PRIMARY_USER_ID),
                )

                runCurrent()
                verify(dreamManager).startDream()
                job.cancel()
            }
        }

    @Test
    fun testMonitoringUpdatesAndRestart_dreamEndsAfterDelay() =
        with(kosmos) {
            testScope.runTest {
                setActiveUser(PRIMARY_USER)
                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
                whenever(controlsListingController.getCurrentServices())
                    .thenReturn(
                        listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
                    )

                val job = launch { underTest.monitorUpdatesAndRestart() }
                val panelComponent by collectLastValue(underTest.panelComponent)

                assertThat(panelComponent).isEqualTo(TEST_COMPONENT)
                verify(dreamManager, never()).startDream()

                fakeSystemClock.advanceTime(100)
                // The package update is started.
                fakePackageChangeRepository.notifyUpdateStarted(
                    TEST_PACKAGE,
                    UserHandle.of(PRIMARY_USER_ID),
                )
                fakeSystemClock.advanceTime(MAX_UPDATE_CORRELATION_DELAY.inWholeMilliseconds + 100)
                // Task fragment becomes empty as a result of the update.
                underTest.onTaskFragmentEmpty()

                runCurrent()
                verify(dreamManager, never()).startDream()

                fakeSystemClock.advanceTime(500)
                // The package update is finished.
                fakePackageChangeRepository.notifyUpdateFinished(
                    TEST_PACKAGE,
                    UserHandle.of(PRIMARY_USER_ID),
                )

                runCurrent()
                verify(dreamManager, never()).startDream()
                job.cancel()
            }
        }

    private fun runServicesUpdate(hasPanelBoolean: Boolean = true) {
        val listings =
            listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = hasPanelBoolean))
        val callback = withArgCaptor { verify(controlsListingController).addCallback(capture()) }
        val callback = withArgCaptor {
            verify(kosmos.controlsListingController).addCallback(capture())
        }
        callback.onServicesUpdated(listings)
    }

    private suspend fun TestScope.setActiveUser(user: UserInfo) {
        userRepository.setSelectedUserInfo(user)
        kosmos.fakeUserRepository.setSelectedUserInfo(user)
        kosmos.fakeUserTracker.set(listOf(user), 0)
        runCurrent()
    }
Loading