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()