From 7173a3fa5763b006d7b28127ec97e05e358fbb8a Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Tue, 20 Dec 2022 09:49:20 -0500 Subject: [PATCH] Bind panels with lower priority flags When showing a panel in device controls, bind to it (but with lower priority flags). That way, the application is kept in memory and results in shorter latency when relaunching the panel with the TaskView. Use BIND_NOT_PERCEPTIBLE so it will be at lower priority from SystemUI. However, don't use WAIVE_PRIORITY, as by itself, it will kill the app once the Task is finished in the device controls panel. Test: manual Test: atest com.android.systemui.controls Fixes: 263790347 Change-Id: Ia99ade528bfdbfe2f67ecfe78f687f18df8b6cfd --- .../controller/ControlsBindingController.kt | 11 ++++++++++- .../controller/ControlsBindingControllerImpl.kt | 4 ++++ .../controls/controller/ControlsController.kt | 8 ++++++++ .../controller/ControlsControllerImpl.kt | 4 ++++ .../ControlsProviderLifecycleManager.kt | 15 ++++++++++++--- .../controls/ui/ControlsUiControllerImpl.kt | 2 ++ .../ControlsBindingControllerImplTest.kt | 8 ++++++++ .../controller/ControlsControllerImplTest.kt | 6 ++++++ .../ControlsProviderLifecycleManagerTest.kt | 16 ++++++++++++++++ .../controls/ui/ControlsUiControllerImplTest.kt | 9 +++++++++ 10 files changed, 79 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt index eed55315e8362..9b2a224f17e0e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt @@ -51,12 +51,21 @@ interface ControlsBindingController : UserAwareController { fun bindAndLoadSuggested(component: ComponentName, callback: LoadCallback) /** - * Request to bind to the given service. + * Request to bind to the given service. This should only be used for services using the full + * [ControlsProviderService] API, where SystemUI renders the devices' UI. * * @param component The [ComponentName] of the service to bind */ fun bindService(component: ComponentName) + /** + * Bind to a service that provides a Device Controls panel (embedded activity). This will allow + * the app to remain "warm", and reduce latency. + * + * @param component The [ComponentName] of the [ControlsProviderService] to bind. + */ + fun bindServiceForPanel(component: ComponentName) + /** * Send a subscribe message to retrieve status of a set of controls. * diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 2f0fd99337e53..3d6d3356fb553 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -170,6 +170,10 @@ open class ControlsBindingControllerImpl @Inject constructor( retrieveLifecycleManager(component).bindService() } + override fun bindServiceForPanel(component: ComponentName) { + retrieveLifecycleManager(component).bindServiceForPanel() + } + override fun changeUser(newUser: UserHandle) { if (newUser == currentUser) return diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 2f49c3fe863e1..f29f6d0dd0cb0 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -188,6 +188,14 @@ interface ControlsController : UserAwareController { /** See [ControlsUiController.getPreferredSelectedItem]. */ fun getPreferredSelection(): SelectedItem + /** + * Bind to a service that provides a Device Controls panel (embedded activity). This will allow + * the app to remain "warm", and reduce latency. + * + * @param component The [ComponentName] of the [ControlsProviderService] to bind. + */ + fun bindComponentForPanel(componentName: ComponentName) + /** * Interface for structure to pass data to [ControlsFavoritingActivity]. */ diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 80c5f661f9a38..111fcbbe30be8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -477,6 +477,10 @@ class ControlsControllerImpl @Inject constructor ( bindingController.unsubscribe() } + override fun bindComponentForPanel(componentName: ComponentName) { + bindingController.bindServiceForPanel(componentName) + } + override fun addFavorite( componentName: ComponentName, structureName: CharSequence, diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 5b38e5b28be9f..72c3a943c30b8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -78,6 +78,10 @@ class ControlsProviderLifecycleManager( private const val DEBUG = true private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or Context.BIND_NOT_PERCEPTIBLE + // Use BIND_NOT_PERCEPTIBLE so it will be at lower priority from SystemUI. + // However, don't use WAIVE_PRIORITY, as by itself, it will kill the app + // once the Task is finished in the device controls panel. + private val BIND_FLAGS_PANEL = Context.BIND_AUTO_CREATE or Context.BIND_NOT_PERCEPTIBLE } private val intent = Intent().apply { @@ -87,18 +91,19 @@ class ControlsProviderLifecycleManager( }) } - private fun bindService(bind: Boolean) { + private fun bindService(bind: Boolean, forPanel: Boolean = false) { executor.execute { requiresBound = bind if (bind) { - if (bindTryCount != MAX_BIND_RETRIES) { + if (bindTryCount != MAX_BIND_RETRIES && wrapper == null) { if (DEBUG) { Log.d(TAG, "Binding service $intent") } bindTryCount++ try { + val flags = if (forPanel) BIND_FLAGS_PANEL else BIND_FLAGS val bound = context - .bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user) + .bindServiceAsUser(intent, serviceConnection, flags, user) if (!bound) { context.unbindService(serviceConnection) } @@ -279,6 +284,10 @@ class ControlsProviderLifecycleManager( bindService(true) } + fun bindServiceForPanel() { + bindService(bind = true, forPanel = true) + } + /** * Request unbind from the service. */ diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 1e3e5cd1c31cb..6289788f650a7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -232,6 +232,8 @@ class ControlsUiControllerImpl @Inject constructor ( ControlKey(selected.structure.componentName, it.ci.controlId) } controlsController.get().subscribeToFavorites(selected.structure) + } else { + controlsController.get().bindComponentForPanel(selected.componentName) } listingCallback = createCallback(::showControlsView) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index 0a81c38e7448e..ebbe096b0da32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -268,6 +268,14 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { verify(providers[0]).bindService() } + @Test + fun testBindServiceForPanel() { + controller.bindServiceForPanel(TEST_COMPONENT_NAME_1) + executor.runAllReady() + + verify(providers[0]).bindServiceForPanel() + } + @Test fun testSubscribe() { val controlInfo1 = ControlInfo("id_1", "", "", DeviceTypes.TYPE_UNKNOWN) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index 1b34706bd2206..25f471b0d3e01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -919,6 +919,12 @@ class ControlsControllerImplTest : SysuiTestCase() { .getFile(ControlsFavoritePersistenceWrapper.FILE_NAME, context.user.identifier) assertThat(userStructure.file).isNotNull() } + + @Test + fun testBindForPanel() { + controller.bindComponentForPanel(TEST_COMPONENT) + verify(bindingController).bindServiceForPanel(TEST_COMPONENT) + } } private class DidRunRunnable() : Runnable { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index af3f24a1c58a6..da548f7ccef2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -104,6 +104,22 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { assertTrue(context.isBound(componentName)) } + @Test + fun testBindForPanel() { + manager.bindServiceForPanel() + executor.runAllReady() + assertTrue(context.isBound(componentName)) + } + + @Test + fun testUnbindPanelIsUnbound() { + manager.bindServiceForPanel() + executor.runAllReady() + manager.unbindService() + executor.runAllReady() + assertFalse(context.isBound(componentName)) + } + @Test fun testNullBinding() { val mockContext = mock(Context::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt index d172c9a2e6303..edc6882e71c00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt @@ -228,6 +228,15 @@ class ControlsUiControllerImplTest : SysuiTestCase() { verify(controlsController, never()).refreshStatus(any(), any()) } + @Test + fun testPanelBindsForPanel() { + val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls")) + setUpPanel(panel) + + underTest.show(parent, {}, context) + verify(controlsController).bindComponentForPanel(panel.componentName) + } + @Test fun testPanelCallsTaskViewFactoryCreate() { mockLayoutInflater()