Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +41 −34 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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, Loading @@ -532,7 +536,6 @@ open class ClockRegistry( for ((_, info) in availableClocks) { info.manager?.loadPlugin() } isVerifying.set(false) return@launch } Loading @@ -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() Loading @@ -562,7 +569,7 @@ open class ClockRegistry( manager.unloadPlugin() } } isVerifying.set(false) } } } Loading packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +93 −51 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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>() Loading Loading @@ -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(), Loading @@ -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(), Loading @@ -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 Loading @@ -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) Loading @@ -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) } Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading @@ -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)) Loading @@ -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() Loading Loading @@ -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) { Loading Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +41 −34 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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, Loading @@ -532,7 +536,6 @@ open class ClockRegistry( for ((_, info) in availableClocks) { info.manager?.loadPlugin() } isVerifying.set(false) return@launch } Loading @@ -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() Loading @@ -562,7 +569,7 @@ open class ClockRegistry( manager.unloadPlugin() } } isVerifying.set(false) } } } Loading
packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +93 −51 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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>() Loading Loading @@ -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(), Loading @@ -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(), Loading @@ -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 Loading @@ -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) Loading @@ -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) } Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading @@ -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 Loading @@ -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) Loading @@ -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)) Loading @@ -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() Loading Loading @@ -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) { Loading