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

Commit 8efcf7b3 authored by Hawkwood Glazier's avatar Hawkwood Glazier Committed by Automerger Merge Worker
Browse files

Merge "Ensure overlapping calls to verifyLoadedProviders are executed in...

Merge "Ensure overlapping calls to verifyLoadedProviders are executed in sequence" into udc-qpr-dev am: 02ecee40 am: c9a0d711

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/24866521



Change-Id: I18e1f0cd65a3f2f4ec5d7b9418824fa33c93d51a
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 8e10d957 c9a0d711
Loading
Loading
Loading
Loading
+41 −34
Original line number Diff line number Diff line
@@ -507,9 +507,10 @@ open class ClockRegistry(
        }
    }

    private var isVerifying = AtomicBoolean(false)
    private var isQueued = AtomicBoolean(false)
    fun verifyLoadedProviders() {
        val shouldSchedule = isVerifying.compareAndSet(false, true)
        Log.i(TAG, Thread.currentThread().getStackTrace().toString())
        val shouldSchedule = isQueued.compareAndSet(false, true)
        if (!shouldSchedule) {
            logger.tryLog(
                TAG,
@@ -521,6 +522,9 @@ open class ClockRegistry(
        }

        scope.launch(bgDispatcher) {
            // TODO(b/267372164): Use better threading approach when converting to flows
            synchronized(availableClocks) {
                isQueued.set(false)
                if (keepAllLoaded) {
                    logger.tryLog(
                        TAG,
@@ -532,7 +536,6 @@ open class ClockRegistry(
                    for ((_, info) in availableClocks) {
                        info.manager?.loadPlugin()
                    }
                isVerifying.set(false)
                    return@launch
                }

@@ -548,11 +551,15 @@ open class ClockRegistry(
                    for ((_, info) in availableClocks) {
                        info.manager?.unloadPlugin()
                    }
                isVerifying.set(false)
                    return@launch
                }

            logger.tryLog(TAG, LogLevel.INFO, {}, { "verifyLoadedProviders: load currentClock" })
                logger.tryLog(
                    TAG,
                    LogLevel.INFO,
                    {},
                    { "verifyLoadedProviders: load currentClock" }
                )
                val currentManager = currentClock.manager
                currentManager?.loadPlugin()

@@ -562,7 +569,7 @@ open class ClockRegistry(
                        manager.unloadPlugin()
                    }
                }
            isVerifying.set(false)
            }
        }
    }

+93 −51
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.systemui.shared.clocks

import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.graphics.drawable.Drawable
@@ -28,12 +29,11 @@ import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import kotlinx.coroutines.CoroutineDispatcher
@@ -46,6 +46,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -66,7 +67,6 @@ class ClockRegistryTest : SysuiTestCase() {
    @Mock private lateinit var mockDefaultClock: ClockController
    @Mock private lateinit var mockThumbnail: Drawable
    @Mock private lateinit var mockContentResolver: ContentResolver
    @Mock private lateinit var mockPluginLifecycle: PluginLifecycleManager<ClockProviderPlugin>
    private lateinit var fakeDefaultProvider: FakeClockPlugin
    private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
    private lateinit var registry: ClockRegistry
@@ -84,6 +84,41 @@ class ClockRegistryTest : SysuiTestCase() {
        }
    }

    private class FakeLifecycle(
        private val tag: String,
        private val plugin: ClockProviderPlugin?,
    ) : PluginLifecycleManager<ClockProviderPlugin> {
        var onLoad: (() -> Unit)? = null
        var onUnload: (() -> Unit)? = null

        private var mIsLoaded: Boolean = true
        override fun isLoaded() = mIsLoaded
        override fun getPlugin(): ClockProviderPlugin? = if (isLoaded) plugin else null

        var mComponentName = ComponentName("Package[$tag]", "Class[$tag]")
        override fun toString() = "Manager[$tag]"
        override fun getPackage(): String = mComponentName.getPackageName()
        override fun getComponentName(): ComponentName = mComponentName

        private var isDebug: Boolean = false
        override fun getIsDebug(): Boolean = isDebug
        override fun setIsDebug(value: Boolean) { isDebug = value }

        override fun loadPlugin() {
            if (!mIsLoaded) {
                mIsLoaded = true
                onLoad?.invoke()
            }
        }

        override fun unloadPlugin() {
            if (mIsLoaded) {
                mIsLoaded = false
                onUnload?.invoke()
            }
        }
    }

    private class FakeClockPlugin : ClockProviderPlugin {
        private val metadata = mutableListOf<ClockMetadata>()
        private val createCallbacks = mutableMapOf<ClockId, (ClockId) -> ClockController>()
@@ -150,13 +185,15 @@ class ClockRegistryTest : SysuiTestCase() {
        val plugin1 = FakeClockPlugin()
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")
        val lifecycle1 = FakeLifecycle("1", plugin1)

        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3")
            .addClock("clock_4", "clock 4")
        val lifecycle2 = FakeLifecycle("2", plugin2)

        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
        val list = registry.getClocks()
        assertEquals(
            list.toSet(),
@@ -178,18 +215,18 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() {
        val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val plugin1 = FakeClockPlugin()
            .addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
            .addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
        val lifecycle1 = spy(FakeLifecycle("1", plugin1))

        val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val plugin2 = FakeClockPlugin()
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")
        val lifecycle2 = spy(FakeLifecycle("2", plugin2))

        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
        val list = registry.getClocks()
        assertEquals(
            list.toSet(),
@@ -204,8 +241,8 @@ class ClockRegistryTest : SysuiTestCase() {
        assertEquals(registry.createExampleClock("clock_2"), mockClock)
        assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
        assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
        verify(mockPluginLifecycle1, never()).unloadPlugin()
        verify(mockPluginLifecycle2, times(2)).unloadPlugin()
        verify(lifecycle1, never()).unloadPlugin()
        verify(lifecycle2, times(2)).unloadPlugin()
    }

    @Test
@@ -213,14 +250,16 @@ class ClockRegistryTest : SysuiTestCase() {
        val plugin1 = FakeClockPlugin()
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")
        val lifecycle1 = spy(FakeLifecycle("1", plugin1))

        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3", { mockClock })
            .addClock("clock_4", "clock 4")
        val lifecycle2 = spy(FakeLifecycle("2", plugin2))

        registry.applySettings(ClockSettings("clock_3", null))
        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)

        val clock = registry.createCurrentClock()
        assertEquals(mockClock, clock)
@@ -231,17 +270,19 @@ class ClockRegistryTest : SysuiTestCase() {
        val plugin1 = FakeClockPlugin()
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")
        val lifecycle1 = spy(FakeLifecycle("1", plugin1))

        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3", { mockClock })
            .addClock("clock_4", "clock 4")
        val lifecycle2 = spy(FakeLifecycle("2", plugin2))

        registry.applySettings(ClockSettings("clock_3", null))

        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        assertEquals(DEFAULT_CLOCK_ID, registry.activeClockId)

        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
        assertEquals("clock_3", registry.activeClockId)
    }

@@ -250,15 +291,17 @@ class ClockRegistryTest : SysuiTestCase() {
        val plugin1 = FakeClockPlugin()
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")
        val lifecycle1 = spy(FakeLifecycle("1", plugin1))

        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3")
            .addClock("clock_4", "clock 4")
        val lifecycle2 = spy(FakeLifecycle("2", plugin2))

        registry.applySettings(ClockSettings("clock_3", null))
        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
        pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
        pluginListener.onPluginUnloaded(plugin2, lifecycle2)

        val clock = registry.createCurrentClock()
        assertEquals(clock, mockDefaultClock)
@@ -266,15 +309,15 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun pluginRemoved_clockAndListChanged() {
        val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val plugin1 = FakeClockPlugin()
            .addClock("clock_1", "clock 1")
            .addClock("clock_2", "clock 2")
        val lifecycle1 = spy(FakeLifecycle("1", plugin1))

        val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val plugin2 = FakeClockPlugin()
            .addClock("clock_3", "clock 3", { mockClock })
            .addClock("clock_4", "clock 4")
        val lifecycle2 = spy(FakeLifecycle("2", plugin2))

        var changeCallCount = 0
        var listChangeCallCount = 0
@@ -288,32 +331,32 @@ class ClockRegistryTest : SysuiTestCase() {
        assertEquals(1, changeCallCount)
        assertEquals(0, listChangeCallCount)

        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        scheduler.runCurrent()
        assertEquals(1, changeCallCount)
        assertEquals(1, listChangeCallCount)

        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
        scheduler.runCurrent()
        assertEquals(2, changeCallCount)
        assertEquals(2, listChangeCallCount)

        pluginListener.onPluginUnloaded(plugin1, mockPluginLifecycle1)
        pluginListener.onPluginUnloaded(plugin1, lifecycle1)
        scheduler.runCurrent()
        assertEquals(2, changeCallCount)
        assertEquals(2, listChangeCallCount)

        pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle2)
        pluginListener.onPluginUnloaded(plugin2, lifecycle2)
        scheduler.runCurrent()
        assertEquals(3, changeCallCount)
        assertEquals(2, listChangeCallCount)

        pluginListener.onPluginDetached(mockPluginLifecycle1)
        pluginListener.onPluginDetached(lifecycle1)
        scheduler.runCurrent()
        assertEquals(3, changeCallCount)
        assertEquals(3, listChangeCallCount)

        pluginListener.onPluginDetached(mockPluginLifecycle2)
        pluginListener.onPluginDetached(lifecycle2)
        scheduler.runCurrent()
        assertEquals(3, changeCallCount)
        assertEquals(4, listChangeCallCount)
@@ -321,8 +364,9 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun unknownPluginAttached_clockAndListUnchanged_loadRequested() {
        val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        whenever(mockPluginLifecycle.getPackage()).thenReturn("some.other.package")
        val lifecycle = FakeLifecycle("", null).apply {
            mComponentName = ComponentName("some.other.package", "SomeClass")
        }

        var changeCallCount = 0
        var listChangeCallCount = 0
@@ -331,7 +375,7 @@ class ClockRegistryTest : SysuiTestCase() {
            override fun onAvailableClocksChanged() { listChangeCallCount++ }
        })

        assertEquals(true, pluginListener.onPluginAttached(mockPluginLifecycle))
        assertEquals(true, pluginListener.onPluginAttached(lifecycle))
        scheduler.runCurrent()
        assertEquals(0, changeCallCount)
        assertEquals(0, listChangeCallCount)
@@ -339,10 +383,12 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun knownPluginAttached_clockAndListChanged_notLoaded() {
        val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.clocks.metro")
        val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.clocks.bignum")
        val lifecycle1 = FakeLifecycle("Metro", null).apply {
            mComponentName = ComponentName("com.android.systemui.clocks.metro", "MetroClock")
        }
        val lifecycle2 = FakeLifecycle("BigNum", null).apply {
            mComponentName = ComponentName("com.android.systemui.clocks.bignum", "BigNumClock")
        }

        var changeCallCount = 0
        var listChangeCallCount = 0
@@ -356,12 +402,12 @@ class ClockRegistryTest : SysuiTestCase() {
        assertEquals(1, changeCallCount)
        assertEquals(0, listChangeCallCount)

        assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle1))
        assertEquals(false, pluginListener.onPluginAttached(lifecycle1))
        scheduler.runCurrent()
        assertEquals(1, changeCallCount)
        assertEquals(1, listChangeCallCount)

        assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle2))
        assertEquals(false, pluginListener.onPluginAttached(lifecycle2))
        scheduler.runCurrent()
        assertEquals(1, changeCallCount)
        assertEquals(2, listChangeCallCount)
@@ -369,18 +415,14 @@ class ClockRegistryTest : SysuiTestCase() {

    @Test
    fun pluginAddRemove_concurrentModification() {
        val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val mockPluginLifecycle3 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val mockPluginLifecycle4 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
        val lifecycle1 = FakeLifecycle("1", plugin1)
        val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
        val lifecycle2 = FakeLifecycle("2", plugin2)
        val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
        val lifecycle3 = FakeLifecycle("3", plugin3)
        val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
        whenever(mockPluginLifecycle1.isLoaded).thenReturn(true)
        whenever(mockPluginLifecycle2.isLoaded).thenReturn(true)
        whenever(mockPluginLifecycle3.isLoaded).thenReturn(true)
        whenever(mockPluginLifecycle4.isLoaded).thenReturn(true)
        val lifecycle4 = FakeLifecycle("4", plugin4)

        // Set the current clock to the final clock to load
        registry.applySettings(ClockSettings("clock_4", null))
@@ -390,15 +432,15 @@ class ClockRegistryTest : SysuiTestCase() {
        // unload other plugins. This causes ClockRegistry to modify the list of available clock
        // plugins while it is being iterated over. In production this happens as a result of a
        // thread race, instead of synchronously like it does here.
        whenever(mockPluginLifecycle2.unloadPlugin()).then {
            pluginListener.onPluginDetached(mockPluginLifecycle1)
            pluginListener.onPluginLoaded(plugin4, mockContext, mockPluginLifecycle4)
        lifecycle2.onUnload = {
            pluginListener.onPluginDetached(lifecycle1)
            pluginListener.onPluginLoaded(plugin4, mockContext, lifecycle4)
        }

        // Load initial plugins
        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
        pluginListener.onPluginLoaded(plugin3, mockContext, mockPluginLifecycle3)
        pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
        pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
        pluginListener.onPluginLoaded(plugin3, mockContext, lifecycle3)

        // Repeatedly verify the loaded providers to get final state
        registry.verifyLoadedProviders()
@@ -484,11 +526,11 @@ class ClockRegistryTest : SysuiTestCase() {
    private fun testTransitClockFlag(flag: Boolean) {
        featureFlags.set(TRANSIT_CLOCK, flag)
        registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
        val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
        val plugin = FakeClockPlugin()
                .addClock("clock_1", "clock 1")
                .addClock("DIGITAL_CLOCK_METRO", "metro clock")
        pluginListener.onPluginLoaded(plugin, mockContext, mockPluginLifecycle)
        val lifecycle = FakeLifecycle("metro", plugin)
        pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)

        val list = registry.getClocks()
        if (flag) {