From f1b8e7a94c371c727d2eb96d334c787cc556f77c Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Thu, 2 Mar 2023 13:03:10 -0500 Subject: [PATCH] Force reload listings for ControlsStartable This makes sure that by the time ControlsStartable needs to know about the services, they have been fetched at least once. The call to ControlsListingController#forceReload should happen in the background thread. Fixes: 271289611 Test: atest ControlsListingControllerImplTest Test: atest ControlsStartableTest Test: manual, adb shell dumpsys activity services Change-Id: Ia6228854dbbc426cc50ef0626efc29ee3ac7bf44 --- .../management/ControlsListingController.kt | 4 + .../ControlsListingControllerImpl.kt | 50 ++++++++--- .../controls/start/ControlsStartable.kt | 5 +- .../ControlsListingControllerImplTest.kt | 89 ++++++++++++++++++- .../controls/start/ControlsStartableTest.kt | 38 +++++--- 5 files changed, 159 insertions(+), 27 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt index b9f16665944fd..cf5ccc5bbf6cb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt @@ -16,6 +16,7 @@ package com.android.systemui.controls.management +import android.annotation.WorkerThread import android.content.ComponentName import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.util.UserAwareController @@ -33,6 +34,9 @@ interface ControlsListingController : */ fun getCurrentServices(): List + @WorkerThread + fun forceReload() + /** * Get the app label for a given component. * diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt index c81a2c7e2ac68..8ba060e02c3df 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt @@ -16,8 +16,11 @@ package com.android.systemui.controls.management +import android.annotation.WorkerThread import android.content.ComponentName import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager import android.os.UserHandle import android.service.controls.ControlsProviderService import android.util.Log @@ -65,7 +68,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( private val serviceListingBuilder: (Context) -> ServiceListing, private val userTracker: UserTracker, dumpManager: DumpManager, - featureFlags: FeatureFlags + private val featureFlags: FeatureFlags ) : ControlsListingController, Dumpable { @Inject @@ -97,18 +100,7 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( // After here, `list` is not captured, so we don't risk modifying it outside of the callback backgroundExecutor.execute { if (userChangeInProgress.get() > 0) return@execute - if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) { - val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED) - newServices.forEach { - it.resolvePanelActivity(allowAllApps) } - } - - if (newServices != availableServices) { - availableServices = newServices - callbacks.forEach { - it.onServicesUpdated(getCurrentServices()) - } - } + updateServices(newServices) } } @@ -120,6 +112,21 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( serviceListing.reload() } + private fun updateServices(newServices: List) { + if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) { + val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED) + newServices.forEach { + it.resolvePanelActivity(allowAllApps) } + } + + if (newServices != availableServices) { + availableServices = newServices + callbacks.forEach { + it.onServicesUpdated(getCurrentServices()) + } + } + } + override fun changeUser(newUser: UserHandle) { userChangeInProgress.incrementAndGet() serviceListing.setListening(false) @@ -178,6 +185,23 @@ class ControlsListingControllerImpl @VisibleForTesting constructor( override fun getCurrentServices(): List = availableServices.map(ControlsServiceInfo::copy) + @WorkerThread + override fun forceReload() { + val packageManager = context.packageManager + val intent = Intent(ControlsProviderService.SERVICE_CONTROLS) + val user = userTracker.userHandle + val flags = PackageManager.GET_SERVICES or + PackageManager.GET_META_DATA or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE or + PackageManager.MATCH_DIRECT_BOOT_AWARE + val services = packageManager.queryIntentServicesAsUser( + intent, + PackageManager.ResolveInfoFlags.of(flags.toLong()), + user + ).map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) } + updateServices(services) + } + /** * Get the localized label for the component. * diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt index 3a4a00c0ccd37..461caccc86d3c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt @@ -19,6 +19,7 @@ package com.android.systemui.controls.start import android.content.Context import android.os.UserHandle +import androidx.annotation.WorkerThread import com.android.systemui.CoreStartable import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.dagger.ControlsComponent @@ -75,11 +76,13 @@ constructor( // Controls is disabled, we don't need this anymore return } - startForUser() + executor.execute(this::startForUser) userTracker.addCallback(userTrackerCallback, executor) } + @WorkerThread private fun startForUser() { + controlsListingController.forceReload() selectDefaultPanelIfNecessary() bindToPanel() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt index 35cd3d2615621..10bfc1b292d34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt @@ -45,6 +45,8 @@ import com.android.systemui.util.mockito.argThat import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import java.util.concurrent.Executor import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNull @@ -62,7 +64,6 @@ import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -import java.util.concurrent.Executor @SmallTest @RunWith(AndroidTestingRunner::class) @@ -106,6 +107,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) `when`(userTracker.userId).thenReturn(user) + `when`(userTracker.userHandle).thenReturn(UserHandle.of(user)) `when`(userTracker.userContext).thenReturn(context) // Return disabled by default `when`(packageManager.getComponentEnabledSetting(any())) @@ -564,6 +566,79 @@ class ControlsListingControllerImplTest : SysuiTestCase() { assertTrue(controller.getCurrentServices().isEmpty()) } + @Test + fun testForceReloadQueriesPackageManager() { + val user = 10 + `when`(userTracker.userHandle).thenReturn(UserHandle.of(user)) + + controller.forceReload() + verify(packageManager).queryIntentServicesAsUser( + argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)), + argThat(FlagsMatcher( + PackageManager.GET_META_DATA.toLong() or + PackageManager.GET_SERVICES.toLong() or + PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong() + )), + eq(UserHandle.of(user)) + ) + } + + @Test + fun testForceReloadUpdatesList() { + val resolveInfo = ResolveInfo() + resolveInfo.serviceInfo = ServiceInfo(componentName) + + `when`(packageManager.queryIntentServicesAsUser( + argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)), + argThat(FlagsMatcher( + PackageManager.GET_META_DATA.toLong() or + PackageManager.GET_SERVICES.toLong() or + PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong() + )), + any() + )).thenReturn(listOf(resolveInfo)) + + controller.forceReload() + + val services = controller.getCurrentServices() + assertThat(services.size).isEqualTo(1) + assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName) + } + + @Test + fun testForceReloadCallsListeners() { + controller.addCallback(mockCallback) + executor.runAllReady() + + @Suppress("unchecked_cast") + val captor: ArgumentCaptor> = + ArgumentCaptor.forClass(List::class.java) + as ArgumentCaptor> + + val resolveInfo = ResolveInfo() + resolveInfo.serviceInfo = ServiceInfo(componentName) + + `when`(packageManager.queryIntentServicesAsUser( + any(), + any(), + any() + )).thenReturn(listOf(resolveInfo)) + + reset(mockCallback) + controller.forceReload() + + verify(mockCallback).onServicesUpdated(capture(captor)) + + val services = captor.value + + assertThat(services.size).isEqualTo(1) + assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName) + } + + + private fun ServiceInfo( componentName: ComponentName, panelActivityComponentName: ComponentName? = null @@ -600,7 +675,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() { private fun setUpQueryResult(infos: List) { `when`( packageManager.queryIntentActivitiesAsUser( - argThat(IntentMatcher(activityName)), + argThat(IntentMatcherComponent(activityName)), argThat(FlagsMatcher(FLAGS)), eq(UserHandle.of(user)) ) @@ -609,7 +684,7 @@ class ControlsListingControllerImplTest : SysuiTestCase() { }) } - private class IntentMatcher( + private class IntentMatcherComponent( private val componentName: ComponentName ) : ArgumentMatcher { override fun matches(argument: Intent?): Boolean { @@ -617,6 +692,14 @@ class ControlsListingControllerImplTest : SysuiTestCase() { } } + private class IntentMatcherAction( + private val action: String + ) : ArgumentMatcher { + override fun matches(argument: Intent?): Boolean { + return argument?.action == action + } + } + private class FlagsMatcher( private val flags: Long ) : ArgumentMatcher { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt index 9d8084d4f2f43..bd7e98e16b91c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt @@ -42,6 +42,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.doReturn import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions @@ -80,9 +82,10 @@ class ControlsStartableTest : SysuiTestCase() { fun testNoPreferredPackagesNoDefaultSelected_noNewSelection() { `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION) val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController, never()).setPreferredSelection(any()) } @@ -92,9 +95,10 @@ class ControlsStartableTest : SysuiTestCase() { whenever(authorizedPanelsRepository.getPreferredPackages()) .thenReturn(setOf(TEST_PACKAGE_PANEL)) `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION) - `when`(controlsListingController.getCurrentServices()).thenReturn(emptyList()) + setUpControlsListingControls(emptyList()) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController, never()).setPreferredSelection(any()) } @@ -105,9 +109,10 @@ class ControlsStartableTest : SysuiTestCase() { .thenReturn(setOf(TEST_PACKAGE_PANEL)) `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION) val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "not panel", hasPanel = false)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController, never()).setPreferredSelection(any()) } @@ -119,9 +124,10 @@ class ControlsStartableTest : SysuiTestCase() { `when`(controlsController.getPreferredSelection()) .thenReturn(mock()) val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController, never()).setPreferredSelection(any()) } @@ -132,9 +138,10 @@ class ControlsStartableTest : SysuiTestCase() { .thenReturn(setOf(TEST_PACKAGE_PANEL)) `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION) val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController).setPreferredSelection(listings[0].toPanelItem()) } @@ -149,9 +156,10 @@ class ControlsStartableTest : SysuiTestCase() { ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true), ControlsServiceInfo(ComponentName("other_package", "cls"), "non panel", false) ) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController).setPreferredSelection(listings[0].toPanelItem()) } @@ -166,9 +174,10 @@ class ControlsStartableTest : SysuiTestCase() { ControlsServiceInfo(ComponentName("other_package", "cls"), "panel", true), ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true) ) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController).setPreferredSelection(listings[1].toPanelItem()) } @@ -176,10 +185,11 @@ class ControlsStartableTest : SysuiTestCase() { @Test fun testPreferredSelectionIsPanel_bindOnStart() { val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) `when`(controlsController.getPreferredSelection()).thenReturn(listings[0].toPanelItem()) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController).bindComponentForPanel(TEST_COMPONENT_PANEL) } @@ -187,11 +197,12 @@ class ControlsStartableTest : SysuiTestCase() { @Test fun testPreferredSelectionPanel_listingNoPanel_notBind() { val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) `when`(controlsController.getPreferredSelection()) .thenReturn(SelectedItem.PanelItem("panel", TEST_COMPONENT_PANEL)) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController, never()).bindComponentForPanel(any()) } @@ -199,10 +210,11 @@ class ControlsStartableTest : SysuiTestCase() { @Test fun testNotPanelSelection_noBind() { val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false)) - `when`(controlsListingController.getCurrentServices()).thenReturn(listings) + setUpControlsListingControls(listings) `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION) createStartable(enabled = true).start() + fakeExecutor.runAllReady() verify(controlsController, never()).bindComponentForPanel(any()) } @@ -221,6 +233,12 @@ class ControlsStartableTest : SysuiTestCase() { verify(controlsController, never()).setPreferredSelection(any()) } + private fun setUpControlsListingControls(listings: List) { + doAnswer { doReturn(listings).`when`(controlsListingController).getCurrentServices() } + .`when`(controlsListingController) + .forceReload() + } + private fun createStartable(enabled: Boolean): ControlsStartable { val component: ControlsComponent = mock() {