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

Commit 2ad4469c authored by Matt Pietal's avatar Matt Pietal
Browse files

Smartspace - Don't connect until setup

SmartspaceService attempts to cache connections until the proper
remote service comes online. However, this cache can be cleared,
dropping any prior connections without informing them. This leaves
smartspace in a state where it will never be updated until after
reboot, and happens after SUW most prominently.

Delay connecting until the device is provisioned and the user is
completely setup to avoid this early connection issue.

Fixes: 190462561
Test: atest LockscreenSmartspaceControllerTest
Change-Id: I4081136c76488874cb50de58dfb8484bb19b23bb
(cherry picked from commit bb0f2900)
(cherry picked from commit b8ab7211)
parent 3ab2c7fb
Loading
Loading
Loading
Loading
+63 −41
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.settings.SecureSettings
import java.lang.RuntimeException
@@ -67,6 +68,7 @@ class LockscreenSmartspaceController @Inject constructor(
    private val contentResolver: ContentResolver,
    private val configurationController: ConfigurationController,
    private val statusBarStateController: StatusBarStateController,
    private val deviceProvisionedController: DeviceProvisionedController,
    private val execution: Execution,
    @Main private val uiExecutor: Executor,
    @Main private val handler: Handler,
@@ -95,6 +97,55 @@ class LockscreenSmartspaceController @Inject constructor(
        }
    }

    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
        execution.assertIsMainThread()
        val filteredTargets = targets.filter(::filterSmartspaceTarget)
        plugin?.onTargetsAvailable(filteredTargets)
    }

    private val userTrackerCallback = object : UserTracker.Callback {
        override fun onUserChanged(newUser: Int, userContext: Context) {
            execution.assertIsMainThread()
            reloadSmartspace()
        }
    }

    private val settingsObserver = object : ContentObserver(handler) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            execution.assertIsMainThread()
            reloadSmartspace()
        }
    }

    private val configChangeListener = object : ConfigurationController.ConfigurationListener {
        override fun onThemeChanged() {
            execution.assertIsMainThread()
            updateTextColorFromWallpaper()
        }
    }

    private val statusBarStateListener = object : StatusBarStateController.StateListener {
        override fun onDozeAmountChanged(linear: Float, eased: Float) {
            execution.assertIsMainThread()
            smartspaceViews.forEach { it.setDozeAmount(eased) }
        }
    }

    private val deviceProvisionedListener =
        object : DeviceProvisionedController.DeviceProvisionedListener {
            override fun onDeviceProvisionedChanged() {
                connectSession()
            }

            override fun onUserSetupChanged() {
                connectSession()
            }
        }

    init {
        deviceProvisionedController.addCallback(deviceProvisionedListener)
    }

    fun isEnabled(): Boolean {
        execution.assertIsMainThread()

@@ -145,10 +196,20 @@ class LockscreenSmartspaceController @Inject constructor(
        if (plugin == null || session != null) {
            return
        }
        val session = smartspaceManager.createSmartspaceSession(

        // Only connect after the device is fully provisioned to avoid connection caching
        // issues
        if (!deviceProvisionedController.isDeviceProvisioned() ||
                !deviceProvisionedController.isCurrentUserSetup()) {
            return
        }

        val newSession = smartspaceManager.createSmartspaceSession(
                SmartspaceConfig.Builder(context, "lockscreen").build())
        session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
        newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
        this.session = newSession

        deviceProvisionedController.removeCallback(deviceProvisionedListener)
        userTracker.addCallback(userTrackerCallback, uiExecutor)
        contentResolver.registerContentObserver(
                secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -159,8 +220,6 @@ class LockscreenSmartspaceController @Inject constructor(
        configurationController.addCallback(configChangeListener)
        statusBarStateController.addCallback(statusBarStateListener)

        this.session = session

        reloadSmartspace()
    }

@@ -197,43 +256,6 @@ class LockscreenSmartspaceController @Inject constructor(
        plugin?.unregisterListener(listener)
    }

    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
        execution.assertIsMainThread()
        val filteredTargets = targets.filter(::filterSmartspaceTarget)
        plugin?.onTargetsAvailable(filteredTargets)
    }

    private val userTrackerCallback = object : UserTracker.Callback {
        override fun onUserChanged(newUser: Int, userContext: Context) {
            execution.assertIsMainThread()
            reloadSmartspace()
        }

        override fun onProfilesChanged(profiles: List<UserInfo>) {
        }
    }

    private val settingsObserver = object : ContentObserver(handler) {
        override fun onChange(selfChange: Boolean, uri: Uri?) {
            execution.assertIsMainThread()
            reloadSmartspace()
        }
    }

    private val configChangeListener = object : ConfigurationController.ConfigurationListener {
        override fun onThemeChanged() {
            execution.assertIsMainThread()
            updateTextColorFromWallpaper()
        }
    }

    private val statusBarStateListener = object : StatusBarStateController.StateListener {
        override fun onDozeAmountChanged(linear: Float, eased: Float) {
            execution.assertIsMainThread()
            smartspaceViews.forEach { it.setDozeAmount(eased) }
        }
    }

    private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
        return when (t.userHandle) {
            userTracker.userHandle -> {
+34 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener
import com.android.systemui.util.concurrency.FakeExecution
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -89,6 +91,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    @Mock
    private lateinit var statusBarStateController: StatusBarStateController
    @Mock
    private lateinit var deviceProvisionedController: DeviceProvisionedController
    @Mock
    private lateinit var handler: Handler

    @Mock
@@ -106,12 +110,15 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
    @Captor
    private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
    @Captor
    private lateinit var deviceProvisionedCaptor: ArgumentCaptor<DeviceProvisionedListener>

    private lateinit var sessionListener: OnTargetsAvailableListener
    private lateinit var userListener: UserTracker.Callback
    private lateinit var settingsObserver: ContentObserver
    private lateinit var configChangeListener: ConfigurationListener
    private lateinit var statusBarStateListener: StateListener
    private lateinit var deviceProvisionedListener: DeviceProvisionedListener

    private lateinit var smartspaceView: SmartspaceView

@@ -145,6 +152,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
        `when`(userTracker.userProfiles).thenReturn(userList)
        `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)

        setActiveUser(userHandlePrimary)
        setAllowPrivateNotifications(userHandlePrimary, true)
@@ -162,11 +171,15 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
                contentResolver,
                configurationController,
                statusBarStateController,
                deviceProvisionedController,
                execution,
                executor,
                handler,
                Optional.of(plugin)
                )

        verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor))
        deviceProvisionedListener = deviceProvisionedCaptor.value
    }

    @Test(expected = RuntimeException::class)
@@ -180,6 +193,27 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
        // THEN an exception is thrown
    }

    @Test
    fun connectOnlyAfterDeviceIsProvisioned() {
        // GIVEN an unprovisioned device and an attempt to connect
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)

        // WHEN a connection attempt is made
        controller.buildAndConnectView(fakeParent)

        // THEN no session is created
        verify(smartspaceManager, never()).createSmartspaceSession(any())

        // WHEN it does become provisioned
        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
        deviceProvisionedListener.onUserSetupChanged()

        // THEN the session is created
        verify(smartspaceManager).createSmartspaceSession(any())
    }

    @Test
    fun testListenersAreRegistered() {
        // GIVEN a listener is added after a session is created